diff options
Diffstat (limited to 'src/userfunc.c')
-rw-r--r-- | src/userfunc.c | 917 |
1 files changed, 626 insertions, 291 deletions
diff --git a/src/userfunc.c b/src/userfunc.c index 29e0fac05..808ed968a 100644 --- a/src/userfunc.c +++ b/src/userfunc.c @@ -22,8 +22,8 @@ #define FC_DELETED 0x10 // :delfunction used while uf_refcount > 0 #define FC_REMOVED 0x20 // function redefined while uf_refcount > 0 #define FC_SANDBOX 0x40 // function defined in the sandbox - -#define FUNCARG(fp, j) ((char_u **)(fp->uf_args.ga_data))[j] +#define FC_DEAD 0x80 // function kept only for reference to dfunc +#define FC_EXPORT 0x100 // "export def Func()" /* * All user-defined functions are found in this hashtable. @@ -63,13 +63,84 @@ func_tbl_get(void) } /* + * Get one function argument and an optional type: "arg: type". + * Return a pointer to after the type. + * When something is wrong return "arg". + */ + static char_u * +one_function_arg(char_u *arg, garray_T *newargs, garray_T *argtypes, int skip) +{ + char_u *p = arg; + + while (ASCII_ISALNUM(*p) || *p == '_') + ++p; + if (arg == p || isdigit(*arg) + || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) + || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) + { + if (!skip) + semsg(_("E125: Illegal argument: %s"), arg); + return arg; + } + if (newargs != NULL && ga_grow(newargs, 1) == FAIL) + return arg; + if (newargs != NULL) + { + char_u *arg_copy; + int c; + int i; + + c = *p; + *p = NUL; + arg_copy = vim_strsave(arg); + if (arg_copy == NULL) + { + *p = c; + return arg; + } + + // Check for duplicate argument name. + for (i = 0; i < newargs->ga_len; ++i) + if (STRCMP(((char_u **)(newargs->ga_data))[i], arg_copy) == 0) + { + semsg(_("E853: Duplicate argument name: %s"), arg_copy); + vim_free(arg_copy); + return arg; + } + ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg_copy; + newargs->ga_len++; + + *p = c; + } + + // get any type from "arg: type" + if (argtypes != NULL && ga_grow(argtypes, 1) == OK) + { + char_u *type = NULL; + + if (*p == ':') + { + type = skipwhite(p + 1); + p = skip_type(type); + type = vim_strnsave(type, p - type); + } + else if (*skipwhite(p) == ':') + emsg(_("E1059: No white space allowed before :")); + ((char_u **)argtypes->ga_data)[argtypes->ga_len++] = type; + } + + return p; +} + +/* * Get function arguments. */ - static int + int get_function_args( char_u **argp, char_u endchar, garray_T *newargs, + garray_T *argtypes, // NULL unless using :def int *varargs, garray_T *default_args, int skip) @@ -78,12 +149,13 @@ get_function_args( char_u *arg = *argp; char_u *p = arg; int c; - int i; int any_default = FALSE; char_u *expr; if (newargs != NULL) ga_init2(newargs, (int)sizeof(char_u *), 3); + if (argtypes != NULL) + ga_init2(argtypes, (int)sizeof(char_u *), 3); if (default_args != NULL) ga_init2(default_args, (int)sizeof(char_u *), 3); @@ -101,46 +173,29 @@ get_function_args( *varargs = TRUE; p += 3; mustend = TRUE; + + if (argtypes != NULL) + { + // ...name: list<type> + if (!ASCII_ISALPHA(*p)) + { + emsg(_("E1055: Missing name after ...")); + break; + } + + arg = p; + p = one_function_arg(p, newargs, argtypes, skip); + if (p == arg) + break; + } } else { arg = p; - while (ASCII_ISALNUM(*p) || *p == '_') - ++p; - if (arg == p || isdigit(*arg) - || (p - arg == 9 && STRNCMP(arg, "firstline", 9) == 0) - || (p - arg == 8 && STRNCMP(arg, "lastline", 8) == 0)) - { - if (!skip) - semsg(_("E125: Illegal argument: %s"), arg); + p = one_function_arg(p, newargs, argtypes, skip); + if (p == arg) break; - } - if (newargs != NULL && ga_grow(newargs, 1) == FAIL) - goto err_ret; - if (newargs != NULL) - { - c = *p; - *p = NUL; - arg = vim_strsave(arg); - if (arg == NULL) - { - *p = c; - goto err_ret; - } - - // Check for duplicate argument name. - for (i = 0; i < newargs->ga_len; ++i) - if (STRCMP(((char_u **)(newargs->ga_data))[i], arg) == 0) - { - semsg(_("E853: Duplicate argument name: %s"), arg); - vim_free(arg); - goto err_ret; - } - ((char_u **)(newargs->ga_data))[newargs->ga_len] = arg; - newargs->ga_len++; - *p = c; - } if (*skipwhite(p) == '=' && default_args != NULL) { typval_T rettv; @@ -266,7 +321,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) ga_init(&newlines); // First, check if this is a lambda expression. "->" must exist. - ret = get_function_args(&start, '-', NULL, NULL, NULL, TRUE); + ret = get_function_args(&start, '-', NULL, NULL, NULL, NULL, TRUE); if (ret == FAIL || *start != '>') return NOTDONE; @@ -276,7 +331,8 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) else pnewargs = NULL; *arg = skipwhite(*arg + 1); - ret = get_function_args(arg, '-', pnewargs, &varargs, NULL, FALSE); + // TODO: argument types + ret = get_function_args(arg, '-', pnewargs, NULL, &varargs, NULL, FALSE); if (ret == FAIL || **arg != '>') goto errret; @@ -307,6 +363,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) goto errret; + fp->uf_dfunc_idx = -1; pt = ALLOC_CLEAR_ONE(partial_T); if (pt == NULL) goto errret; @@ -345,6 +402,7 @@ get_lambda_tv(char_u **arg, typval_T *rettv, int evaluate) #endif if (sandbox) flags |= FC_SANDBOX; + // can be called with more args than uf_args.ga_len fp->uf_varargs = TRUE; fp->uf_flags = flags; fp->uf_calls = 0; @@ -581,17 +639,73 @@ fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) } /* + * Find a function "name" in script "sid". + */ + static ufunc_T * +find_func_with_sid(char_u *name, int sid) +{ + hashitem_T *hi; + char_u buffer[200]; + + buffer[0] = K_SPECIAL; + buffer[1] = KS_EXTRA; + buffer[2] = (int)KE_SNR; + vim_snprintf((char *)buffer + 3, sizeof(buffer) - 3, "%ld_%s", + (long)sid, name); + hi = hash_find(&func_hashtab, buffer); + if (!HASHITEM_EMPTY(hi)) + return HI2UF(hi); + + return NULL; +} + +/* * Find a function by name, return pointer to it in ufuncs. * Return NULL for unknown function. */ - ufunc_T * -find_func(char_u *name) + static ufunc_T * +find_func_even_dead(char_u *name, cctx_T *cctx) { hashitem_T *hi; + ufunc_T *func; + imported_T *imported; + + if (in_vim9script()) + { + // Find script-local function before global one. + func = find_func_with_sid(name, current_sctx.sc_sid); + if (func != NULL) + return func; + + // Find imported funcion before global one. + imported = find_imported(name, cctx); + if (imported != NULL && imported->imp_funcname != NULL) + { + hi = hash_find(&func_hashtab, imported->imp_funcname); + if (!HASHITEM_EMPTY(hi)) + return HI2UF(hi); + } + } hi = hash_find(&func_hashtab, name); if (!HASHITEM_EMPTY(hi)) return HI2UF(hi); + + return NULL; +} + +/* + * Find a function by name, return pointer to it in ufuncs. + * "cctx" is passed in a :def function to find imported functions. + * Return NULL for unknown or dead function. + */ + ufunc_T * +find_func(char_u *name, cctx_T *cctx) +{ + ufunc_T *fp = find_func_even_dead(name, cctx); + + if (fp != NULL && (fp->uf_flags & FC_DEAD) == 0) + return fp; return NULL; } @@ -761,6 +875,127 @@ cleanup_function_call(funccall_T *fc) } } } +/* + * Unreference "fc": decrement the reference count and free it when it + * becomes zero. "fp" is detached from "fc". + * When "force" is TRUE we are exiting. + */ + static void +funccal_unref(funccall_T *fc, ufunc_T *fp, int force) +{ + funccall_T **pfc; + int i; + + if (fc == NULL) + return; + + if (--fc->fc_refcount <= 0 && (force || ( + fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT + && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT + && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) + for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) + { + if (fc == *pfc) + { + *pfc = fc->caller; + free_funccal_contents(fc); + return; + } + } + for (i = 0; i < fc->fc_funcs.ga_len; ++i) + if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) + ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; +} + +/* + * Remove the function from the function hashtable. If the function was + * deleted while it still has references this was already done. + * Return TRUE if the entry was deleted, FALSE if it wasn't found. + */ + static int +func_remove(ufunc_T *fp) +{ + hashitem_T *hi; + + // Return if it was already virtually deleted. + if (fp->uf_flags & FC_DEAD) + return FALSE; + + hi = hash_find(&func_hashtab, UF2HIKEY(fp)); + if (!HASHITEM_EMPTY(hi)) + { + // When there is a def-function index do not actually remove the + // function, so we can find the index when defining the function again. + if (fp->uf_dfunc_idx >= 0) + fp->uf_flags |= FC_DEAD; + else + hash_remove(&func_hashtab, hi); + return TRUE; + } + return FALSE; +} + + static void +func_clear_items(ufunc_T *fp) +{ + ga_clear_strings(&(fp->uf_args)); + ga_clear_strings(&(fp->uf_def_args)); + ga_clear_strings(&(fp->uf_lines)); + VIM_CLEAR(fp->uf_name_exp); + VIM_CLEAR(fp->uf_arg_types); + ga_clear(&fp->uf_type_list); +#ifdef FEAT_PROFILE + VIM_CLEAR(fp->uf_tml_count); + VIM_CLEAR(fp->uf_tml_total); + VIM_CLEAR(fp->uf_tml_self); +#endif +} + +/* + * Free all things that a function contains. Does not free the function + * itself, use func_free() for that. + * When "force" is TRUE we are exiting. + */ + static void +func_clear(ufunc_T *fp, int force) +{ + if (fp->uf_cleared) + return; + fp->uf_cleared = TRUE; + + // clear this function + func_clear_items(fp); + funccal_unref(fp->uf_scoped, fp, force); + delete_def_function(fp); +} + +/* + * Free a function and remove it from the list of functions. Does not free + * what a function contains, call func_clear() first. + */ + static void +func_free(ufunc_T *fp) +{ + // Only remove it when not done already, otherwise we would remove a newer + // version of the function with the same name. + if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) + func_remove(fp); + + if ((fp->uf_flags & FC_DEAD) == 0) + vim_free(fp); +} + +/* + * Free all things that a function contains and free the function itself. + * When "force" is TRUE we are exiting. + */ + static void +func_clear_free(ufunc_T *fp, int force) +{ + func_clear(fp, force); + func_free(fp); +} + /* * Call a user function. @@ -822,6 +1057,20 @@ call_user_func( ga_init2(&fc->fc_funcs, sizeof(ufunc_T *), 1); func_ptr_ref(fp); + if (fp->uf_dfunc_idx >= 0) + { + estack_push_ufunc(ETYPE_UFUNC, fp, 1); + + // Execute the compiled function. + call_def_function(fp, argcount, argvars, rettv); + --depth; + current_funccal = fc->caller; + + estack_pop(); + free_funccal(fc); + return; + } + if (STRNCMP(fp->uf_name, "<lambda>", 8) == 0) islambda = TRUE; @@ -1146,110 +1395,57 @@ call_user_func( } /* - * Unreference "fc": decrement the reference count and free it when it - * becomes zero. "fp" is detached from "fc". - * When "force" is TRUE we are exiting. + * Call a user function after checking the arguments. */ - static void -funccal_unref(funccall_T *fc, ufunc_T *fp, int force) + int +call_user_func_check( + ufunc_T *fp, + int argcount, + typval_T *argvars, + typval_T *rettv, + funcexe_T *funcexe, + dict_T *selfdict) { - funccall_T **pfc; - int i; - - if (fc == NULL) - return; + int error; + int regular_args = fp->uf_args.ga_len; + + if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) + *funcexe->doesrange = TRUE; + if (argcount < regular_args - fp->uf_def_args.ga_len) + error = FCERR_TOOFEW; + else if (!has_varargs(fp) && argcount > regular_args) + error = FCERR_TOOMANY; + else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) + error = FCERR_DICT; + else + { + int did_save_redo = FALSE; + save_redo_T save_redo; - if (--fc->fc_refcount <= 0 && (force || ( - fc->l_varlist.lv_refcount == DO_NOT_FREE_CNT - && fc->l_vars.dv_refcount == DO_NOT_FREE_CNT - && fc->l_avars.dv_refcount == DO_NOT_FREE_CNT))) - for (pfc = &previous_funccal; *pfc != NULL; pfc = &(*pfc)->caller) + /* + * Call the user function. + * Save and restore search patterns, script variables and + * redo buffer. + */ + save_search_patterns(); + if (!ins_compl_active()) { - if (fc == *pfc) - { - *pfc = fc->caller; - free_funccal_contents(fc); - return; - } + saveRedobuff(&save_redo); + did_save_redo = TRUE; } - for (i = 0; i < fc->fc_funcs.ga_len; ++i) - if (((ufunc_T **)(fc->fc_funcs.ga_data))[i] == fp) - ((ufunc_T **)(fc->fc_funcs.ga_data))[i] = NULL; -} - -/* - * Remove the function from the function hashtable. If the function was - * deleted while it still has references this was already done. - * Return TRUE if the entry was deleted, FALSE if it wasn't found. - */ - static int -func_remove(ufunc_T *fp) -{ - hashitem_T *hi = hash_find(&func_hashtab, UF2HIKEY(fp)); - - if (!HASHITEM_EMPTY(hi)) - { - hash_remove(&func_hashtab, hi); - return TRUE; + ++fp->uf_calls; + call_user_func(fp, argcount, argvars, rettv, + funcexe->firstline, funcexe->lastline, + (fp->uf_flags & FC_DICT) ? selfdict : NULL); + if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) + // Function was unreferenced while being used, free it now. + func_clear_free(fp, FALSE); + if (did_save_redo) + restoreRedobuff(&save_redo); + restore_search_patterns(); + error = FCERR_NONE; } - return FALSE; -} - - static void -func_clear_items(ufunc_T *fp) -{ - ga_clear_strings(&(fp->uf_args)); - ga_clear_strings(&(fp->uf_def_args)); - ga_clear_strings(&(fp->uf_lines)); - VIM_CLEAR(fp->uf_name_exp); -#ifdef FEAT_PROFILE - VIM_CLEAR(fp->uf_tml_count); - VIM_CLEAR(fp->uf_tml_total); - VIM_CLEAR(fp->uf_tml_self); -#endif -} - -/* - * Free all things that a function contains. Does not free the function - * itself, use func_free() for that. - * When "force" is TRUE we are exiting. - */ - static void -func_clear(ufunc_T *fp, int force) -{ - if (fp->uf_cleared) - return; - fp->uf_cleared = TRUE; - - // clear this function - func_clear_items(fp); - funccal_unref(fp->uf_scoped, fp, force); -} - -/* - * Free a function and remove it from the list of functions. Does not free - * what a function contains, call func_clear() first. - */ - static void -func_free(ufunc_T *fp) -{ - // only remove it when not done already, otherwise we would remove a newer - // version of the function - if ((fp->uf_flags & (FC_DELETED | FC_REMOVED)) == 0) - func_remove(fp); - - vim_free(fp); -} - -/* - * Free all things that a function contains and free the function itself. - * When "force" is TRUE we are exiting. - */ - static void -func_clear_free(ufunc_T *fp, int force) -{ - func_clear(fp, force); - func_free(fp); + return error; } /* @@ -1327,9 +1523,13 @@ free_all_functions(void) for (hi = func_hashtab.ht_array; todo > 0; ++hi) if (!HASHITEM_EMPTY(hi)) { + // clear the def function index now + fp = HI2UF(hi); + fp->uf_flags &= ~FC_DEAD; + fp->uf_dfunc_idx = -1; + // Only free functions that are not refcounted, those are // supposed to be freed when no longer referenced. - fp = HI2UF(hi); if (func_name_refcount(fp->uf_name)) ++skipped; else @@ -1371,6 +1571,8 @@ free_all_functions(void) } if (skipped == 0) hash_clear(&func_hashtab); + + free_def_functions(); } #endif @@ -1379,7 +1581,7 @@ free_all_functions(void) * lower case letter and doesn't contain AUTOLOAD_CHAR. * "len" is the length of "name", or -1 for NUL terminated. */ - static int + int builtin_function(char_u *name, int len) { char_u *p; @@ -1469,6 +1671,43 @@ call_callback( } /* + * Give an error message for the result of a function. + * Nothing if "error" is FCERR_NONE. + */ + void +user_func_error(int error, char_u *name) +{ + switch (error) + { + case FCERR_UNKNOWN: + emsg_funcname(e_unknownfunc, name); + break; + case FCERR_NOTMETHOD: + emsg_funcname( + N_("E276: Cannot use function as a method: %s"), name); + break; + case FCERR_DELETED: + emsg_funcname(N_(e_func_deleted), name); + break; + case FCERR_TOOMANY: + emsg_funcname((char *)e_toomanyarg, name); + break; + case FCERR_TOOFEW: + emsg_funcname((char *)e_toofewarg, name); + break; + case FCERR_SCRIPT: + emsg_funcname( + N_("E120: Using <SID> not in a script context: %s"), name); + break; + case FCERR_DICT: + emsg_funcname( + N_("E725: Calling dict function without Dictionary: %s"), + name); + break; + } +} + +/* * Call a function with its resolved parameters * * Return FAIL when the function can't be called, OK otherwise. @@ -1561,7 +1800,7 @@ call_func( if (partial != NULL && partial->pt_func != NULL) fp = partial->pt_func; else - fp = find_func(rfname); + fp = find_func(rfname, NULL); // Trigger FuncUndefined event, may load the function. if (fp == NULL @@ -1570,13 +1809,13 @@ call_func( && !aborting()) { // executed an autocommand, search for the function again - fp = find_func(rfname); + fp = find_func(rfname, NULL); } // Try loading a package. if (fp == NULL && script_autoload(rfname, TRUE) && !aborting()) { // loaded a package, search for the function again - fp = find_func(rfname); + fp = find_func(rfname, NULL); } if (fp != NULL && (fp->uf_flags & FC_DELETED)) @@ -1598,43 +1837,8 @@ call_func( argv_base = 1; } - if (fp->uf_flags & FC_RANGE && funcexe->doesrange != NULL) - *funcexe->doesrange = TRUE; - if (argcount < fp->uf_args.ga_len - fp->uf_def_args.ga_len) - error = FCERR_TOOFEW; - else if (!fp->uf_varargs && argcount > fp->uf_args.ga_len) - error = FCERR_TOOMANY; - else if ((fp->uf_flags & FC_DICT) && selfdict == NULL) - error = FCERR_DICT; - else - { - int did_save_redo = FALSE; - save_redo_T save_redo; - - /* - * Call the user function. - * Save and restore search patterns, script variables and - * redo buffer. - */ - save_search_patterns(); - if (!ins_compl_active()) - { - saveRedobuff(&save_redo); - did_save_redo = TRUE; - } - ++fp->uf_calls; - call_user_func(fp, argcount, argvars, rettv, - funcexe->firstline, funcexe->lastline, - (fp->uf_flags & FC_DICT) ? selfdict : NULL); - if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) - // Function was unreferenced while being used, free it - // now. - func_clear_free(fp, FALSE); - if (did_save_redo) - restoreRedobuff(&save_redo); - restore_search_patterns(); - error = FCERR_NONE; - } + error = call_user_func_check(fp, argcount, argvars, rettv, + funcexe, selfdict); } } else if (funcexe->basetv != NULL) @@ -1675,38 +1879,7 @@ theend: */ if (!aborting()) { - switch (error) - { - case FCERR_UNKNOWN: - emsg_funcname(N_("E117: Unknown function: %s"), name); - break; - case FCERR_NOTMETHOD: - emsg_funcname( - N_("E276: Cannot use function as a method: %s"), - name); - break; - case FCERR_DELETED: - emsg_funcname(N_("E933: Function was deleted: %s"), name); - break; - case FCERR_TOOMANY: - emsg_funcname((char *)e_toomanyarg, name); - break; - case FCERR_TOOFEW: - emsg_funcname( - N_("E119: Not enough arguments for function: %s"), - name); - break; - case FCERR_SCRIPT: - emsg_funcname( - N_("E120: Using <SID> not in a script context: %s"), - name); - break; - case FCERR_DICT: - emsg_funcname( - N_("E725: Calling dict function without Dictionary: %s"), - name); - break; - } + user_func_error(error, name); } // clear the copies made from the partial @@ -1719,6 +1892,12 @@ theend: return ret; } + static char_u * +printable_func_name(ufunc_T *fp) +{ + return fp->uf_name_exp != NULL ? fp->uf_name_exp : fp->uf_name; +} + /* * List the head of the function: "name(arg1, arg2)". */ @@ -1731,16 +1910,21 @@ list_func_head(ufunc_T *fp, int indent) if (indent) msg_puts(" "); msg_puts("function "); - if (fp->uf_name_exp != NULL) - msg_puts((char *)fp->uf_name_exp); - else - msg_puts((char *)fp->uf_name); + msg_puts((char *)printable_func_name(fp)); msg_putchar('('); for (j = 0; j < fp->uf_args.ga_len; ++j) { if (j) msg_puts(", "); msg_puts((char *)FUNCARG(fp, j)); + if (fp->uf_arg_types != NULL) + { + char *tofree; + + msg_puts(": "); + msg_puts(type_name(fp->uf_arg_types[j], &tofree)); + vim_free(tofree); + } if (j >= fp->uf_args.ga_len - fp->uf_def_args.ga_len) { msg_puts(" = "); @@ -1754,6 +1938,21 @@ list_func_head(ufunc_T *fp, int indent) msg_puts(", "); msg_puts("..."); } + if (fp->uf_va_name != NULL) + { + if (j) + msg_puts(", "); + msg_puts("..."); + msg_puts((char *)fp->uf_va_name); + if (fp->uf_va_type) + { + char *tofree; + + msg_puts(": "); + msg_puts(type_name(fp->uf_va_type, &tofree)); + vim_free(tofree); + } + } msg_putchar(')'); if (fp->uf_flags & FC_ABORT) msg_puts(" abort"); @@ -1793,7 +1992,9 @@ trans_function_name( int lead; char_u sid_buf[20]; int len; + int extra = 0; lval_T lv; + int vim9script; if (fdp != NULL) vim_memset(fdp, 0, sizeof(funcdict_T)); @@ -1934,6 +2135,10 @@ trans_function_name( len = (int)(end - lv.ll_name); } + // In Vim9 script a user function is script-local by default. + vim9script = ASCII_ISUPPER(*start) + && current_sctx.sc_version == SCRIPT_VERSION_VIM9; + /* * Copy the function name to allocated memory. * Accept <SID>name() inside a script, translate into <SNR>123_name(). @@ -1941,20 +2146,25 @@ trans_function_name( */ if (skip) lead = 0; // do nothing - else if (lead > 0) + else if (lead > 0 || vim9script) { - lead = 3; - if ((lv.ll_exp_name != NULL && eval_fname_sid(lv.ll_exp_name)) + if (!vim9script) + lead = 3; + if (vim9script || (lv.ll_exp_name != NULL + && eval_fname_sid(lv.ll_exp_name)) || eval_fname_sid(*pp)) { - // It's "s:" or "<SID>" + // It's script-local, "s:" or "<SID>" if (current_sctx.sc_sid <= 0) { emsg(_(e_usingsid)); goto theend; } sprintf((char *)sid_buf, "%ld_", (long)current_sctx.sc_sid); - lead += (int)STRLEN(sid_buf); + if (vim9script) + extra = 3 + (int)STRLEN(sid_buf); + else + lead += (int)STRLEN(sid_buf); } } else if (!(flags & TFN_INT) && builtin_function(lv.ll_name, len)) @@ -1974,19 +2184,19 @@ trans_function_name( } } - name = alloc(len + lead + 1); + name = alloc(len + lead + extra + 1); if (name != NULL) { - if (lead > 0) + if (lead > 0 || vim9script) { name[0] = K_SPECIAL; name[1] = KS_EXTRA; name[2] = (int)KE_SNR; - if (lead > 3) // If it's "<SID>" + if (vim9script || lead > 3) // If it's "<SID>" STRCPY(name + 3, sid_buf); } - mch_memmove(name + lead, lv.ll_name, (size_t)len); - name[lead + len] = NUL; + mch_memmove(name + lead + extra, lv.ll_name, (size_t)len); + name[lead + extra + len] = NUL; } *pp = end; @@ -2012,19 +2222,22 @@ ex_function(exarg_T *eap) char_u *arg; char_u *line_arg = NULL; garray_T newargs; + garray_T argtypes; garray_T default_args; garray_T newlines; int varargs = FALSE; int flags = 0; + char_u *ret_type = NULL; ufunc_T *fp; int overwrite = FALSE; int indent; int nesting; +#define MAX_FUNC_NESTING 50 + char nesting_def[MAX_FUNC_NESTING]; dictitem_T *v; funcdict_T fudi; static int func_nr = 0; // number for nameless function int paren; - hashtab_T *ht; int todo; hashitem_T *hi; int do_concat = TRUE; @@ -2048,7 +2261,8 @@ ex_function(exarg_T *eap) { --todo; fp = HI2UF(hi); - if (message_filtered(fp->uf_name)) + if ((fp->uf_flags & FC_DEAD) + || message_filtered(fp->uf_name)) continue; if (!func_name_refcount(fp->uf_name)) list_func_head(fp, FALSE); @@ -2084,8 +2298,9 @@ ex_function(exarg_T *eap) { --todo; fp = HI2UF(hi); - if (!isdigit(*fp->uf_name) - && vim_regexec(®match, fp->uf_name, 0)) + if ((fp->uf_flags & FC_DEAD) == 0 + && !isdigit(*fp->uf_name) + && vim_regexec(®match, fp->uf_name, 0)) list_func_head(fp, FALSE); } } @@ -2098,6 +2313,10 @@ ex_function(exarg_T *eap) return; } + ga_init(&newargs); + ga_init(&argtypes); + ga_init(&default_args); + /* * Get the function name. There are these situations: * func normal function name @@ -2155,7 +2374,7 @@ ex_function(exarg_T *eap) *p = NUL; if (!eap->skip && !got_int) { - fp = find_func(name); + fp = find_func(name, NULL); if (fp != NULL) { list_func_head(fp, TRUE); @@ -2176,7 +2395,10 @@ ex_function(exarg_T *eap) if (!got_int) { msg_putchar('\n'); - msg_puts(" endfunction"); + if (fp->uf_dfunc_idx >= 0) + msg_puts(" enddef"); + else + msg_puts(" endfunction"); } } else @@ -2231,43 +2453,58 @@ ex_function(exarg_T *eap) emsg(_("E862: Cannot use g: here")); } - if (get_function_args(&p, ')', &newargs, &varargs, - &default_args, eap->skip) == FAIL) + if (get_function_args(&p, ')', &newargs, + eap->cmdidx == CMD_def ? &argtypes : NULL, + &varargs, &default_args, eap->skip) == FAIL) goto errret_2; - // find extra arguments "range", "dict", "abort" and "closure" - for (;;) + if (eap->cmdidx == CMD_def) { - p = skipwhite(p); - if (STRNCMP(p, "range", 5) == 0) + // find the return type: :def Func(): type + if (*p == ':') { - flags |= FC_RANGE; - p += 5; - } - else if (STRNCMP(p, "dict", 4) == 0) - { - flags |= FC_DICT; - p += 4; - } - else if (STRNCMP(p, "abort", 5) == 0) - { - flags |= FC_ABORT; - p += 5; + ret_type = skipwhite(p + 1); + p = skip_type(ret_type); + if (p > ret_type) + p = skipwhite(p); + else + semsg(_("E1056: expected a type: %s"), ret_type); } - else if (STRNCMP(p, "closure", 7) == 0) + } + else + // find extra arguments "range", "dict", "abort" and "closure" + for (;;) { - flags |= FC_CLOSURE; - p += 7; - if (current_funccal == NULL) + p = skipwhite(p); + if (STRNCMP(p, "range", 5) == 0) { - emsg_funcname(N_("E932: Closure function should not be at top level: %s"), - name == NULL ? (char_u *)"" : name); - goto erret; + flags |= FC_RANGE; + p += 5; } + else if (STRNCMP(p, "dict", 4) == 0) + { + flags |= FC_DICT; + p += 4; + } + else if (STRNCMP(p, "abort", 5) == 0) + { + flags |= FC_ABORT; + p += 5; + } + else if (STRNCMP(p, "closure", 7) == 0) + { + flags |= FC_CLOSURE; + p += 7; + if (current_funccal == NULL) + { + emsg_funcname(N_("E932: Closure function should not be at top level: %s"), + name == NULL ? (char_u *)"" : name); + goto erret; + } + } + else + break; } - else - break; - } // When there is a line break use what follows for the function body. // Makes 'exe "func Test()\n...\nendfunc"' work. @@ -2277,7 +2514,8 @@ ex_function(exarg_T *eap) emsg(_(e_trailing)); /* - * Read the body of the function, until ":endfunction" is found. + * Read the body of the function, until "}", ":endfunction" or ":enddef" is + * found. */ if (KeyTyped) { @@ -2288,7 +2526,7 @@ ex_function(exarg_T *eap) { if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL) emsg(_(e_funcdict)); - else if (name != NULL && find_func(name) != NULL) + else if (name != NULL && find_func(name, NULL) != NULL) emsg_funcname(e_funcexts, name); } @@ -2304,6 +2542,7 @@ ex_function(exarg_T *eap) indent = 2; nesting = 0; + nesting_def[nesting] = (eap->cmdidx == CMD_def); for (;;) { if (KeyTyped) @@ -2339,7 +2578,10 @@ ex_function(exarg_T *eap) lines_left = Rows - 1; if (theline == NULL) { - emsg(_("E126: Missing :endfunction")); + if (eap->cmdidx == CMD_def) + emsg(_("E1057: Missing :enddef")); + else + emsg(_("E126: Missing :endfunction")); goto erret; } @@ -2352,7 +2594,7 @@ ex_function(exarg_T *eap) if (skip_until != NULL) { - // Don't check for ":endfunc" between + // Don't check for ":endfunc"/":enddef" between // * ":append" and "." // * ":python <<EOF" and "EOF" // * ":let {var-name} =<< [trim] {marker}" and "{marker}" @@ -2383,8 +2625,9 @@ ex_function(exarg_T *eap) for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p) ; - // Check for "endfunction". - if (checkforcmd(&p, "endfunction", 4) && nesting-- == 0) + // Check for "endfunction" or "enddef". + if (checkforcmd(&p, nesting_def[nesting] + ? "enddef" : "endfunction", 4) && nesting-- == 0) { char_u *nextcmd = NULL; @@ -2393,8 +2636,9 @@ ex_function(exarg_T *eap) else if (line_arg != NULL && *skipwhite(line_arg) != NUL) nextcmd = line_arg; else if (*p != NUL && *p != '"' && p_verbose > 0) - give_warning2( - (char_u *)_("W22: Text found after :endfunction: %s"), + give_warning2(eap->cmdidx == CMD_def + ? (char_u *)_("W1001: Text found after :enddef: %s") + : (char_u *)_("W22: Text found after :endfunction: %s"), p, TRUE); if (nextcmd != NULL) { @@ -2414,7 +2658,7 @@ ex_function(exarg_T *eap) // Increase indent inside "if", "while", "for" and "try", decrease // at "end". - if (indent > 2 && STRNCMP(p, "end", 3) == 0) + if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0)) indent -= 2; else if (STRNCMP(p, "if", 2) == 0 || STRNCMP(p, "wh", 2) == 0 @@ -2423,7 +2667,8 @@ ex_function(exarg_T *eap) indent += 2; // Check for defining a function inside this function. - if (checkforcmd(&p, "function", 2)) + c = *p; + if (checkforcmd(&p, "function", 2) || checkforcmd(&p, "def", 3)) { if (*p == '!') p = skipwhite(p + 1); @@ -2431,8 +2676,14 @@ ex_function(exarg_T *eap) vim_free(trans_function_name(&p, TRUE, 0, NULL, NULL)); if (*skipwhite(p) == '(') { - ++nesting; - indent += 2; + if (nesting == MAX_FUNC_NESTING - 1) + emsg(_("E1058: function nesting too deep")); + else + { + ++nesting; + nesting_def[nesting] = (c == 'd'); + indent += 2; + } } } @@ -2537,6 +2788,8 @@ ex_function(exarg_T *eap) */ if (fudi.fd_dict == NULL) { + hashtab_T *ht; + v = find_var(name, &ht, FALSE); if (v != NULL && v->di_tv.v_type == VAR_FUNC) { @@ -2545,12 +2798,14 @@ ex_function(exarg_T *eap) goto erret; } - fp = find_func(name); + fp = find_func_even_dead(name, NULL); if (fp != NULL) { + int dead = fp->uf_flags & FC_DEAD; + // Function can be replaced with "function!" and when sourcing the // same script again, but only once. - if (!eap->forceit + if (!dead && !eap->forceit && (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid || fp->uf_script_ctx.sc_seq == current_sctx.sc_seq)) { @@ -2582,6 +2837,7 @@ ex_function(exarg_T *eap) fp->uf_name_exp = NULL; func_clear_items(fp); fp->uf_name_exp = exp_name; + fp->uf_flags &= ~FC_DEAD; #ifdef FEAT_PROFILE fp->uf_profiling = FALSE; fp->uf_prof_initialized = FALSE; @@ -2651,6 +2907,7 @@ ex_function(exarg_T *eap) fp = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1); if (fp == NULL) goto erret; + fp->uf_dfunc_idx = -1; if (fudi.fd_dict != NULL) { @@ -2696,6 +2953,61 @@ ex_function(exarg_T *eap) } fp->uf_args = newargs; fp->uf_def_args = default_args; + fp->uf_ret_type = &t_any; + + if (eap->cmdidx == CMD_def) + { + // parse the argument types + ga_init2(&fp->uf_type_list, sizeof(type_T), 5); + + if (argtypes.ga_len > 0) + { + // When "varargs" is set the last name/type goes into uf_va_name + // and uf_va_type. + int len = argtypes.ga_len - (varargs ? 1 : 0); + + fp->uf_arg_types = ALLOC_CLEAR_MULT(type_T *, len); + if (fp->uf_arg_types != NULL) + { + int i; + + for (i = 0; i < len; ++ i) + { + p = ((char_u **)argtypes.ga_data)[i]; + if (p == NULL) + // todo: get type from default value + fp->uf_arg_types[i] = &t_any; + else + fp->uf_arg_types[i] = parse_type(&p, &fp->uf_type_list); + } + } + if (varargs) + { + // Move the last argument "...name: type" to uf_va_name and + // uf_va_type. + fp->uf_va_name = ((char_u **)fp->uf_args.ga_data) + [fp->uf_args.ga_len - 1]; + --fp->uf_args.ga_len; + p = ((char_u **)argtypes.ga_data)[len]; + if (p == NULL) + // todo: get type from default value + fp->uf_va_type = &t_any; + else + fp->uf_va_type = parse_type(&p, &fp->uf_type_list); + } + varargs = FALSE; + } + + // parse the return type, if any + if (ret_type == NULL) + fp->uf_ret_type = &t_void; + else + { + p = ret_type; + fp->uf_ret_type = parse_type(&p, &fp->uf_type_list); + } + } + fp->uf_lines = newlines; if ((flags & FC_CLOSURE) != 0) { @@ -2716,10 +3028,22 @@ ex_function(exarg_T *eap) fp->uf_calls = 0; fp->uf_script_ctx = current_sctx; fp->uf_script_ctx.sc_lnum += sourcing_lnum_top; + if (is_export) + { + fp->uf_flags |= FC_EXPORT; + // let ex_export() know the export worked. + is_export = FALSE; + } + + // ":def Func()" needs to be compiled + if (eap->cmdidx == CMD_def) + compile_def_function(fp, FALSE); + goto ret_free; erret: ga_clear_strings(&newargs); + ga_clear_strings(&argtypes); ga_clear_strings(&default_args); errret_2: ga_clear_strings(&newlines); @@ -2755,7 +3079,17 @@ translated_function_exists(char_u *name) { if (builtin_function(name, -1)) return has_internal_func(name); - return find_func(name) != NULL; + return find_func(name, NULL) != NULL; +} + +/* + * Return TRUE when "ufunc" has old-style "..." varargs + * or named varargs "...name: type". + */ + int +has_varargs(ufunc_T *ufunc) +{ + return ufunc->uf_varargs || ufunc->uf_va_name != NULL; } /* @@ -2826,9 +3160,10 @@ get_user_func_name(expand_T *xp, int idx) ++hi; fp = HI2UF(hi); - if ((fp->uf_flags & FC_DICT) + // don't show dead, dict and lambda functions + if ((fp->uf_flags & FC_DEAD) || (fp->uf_flags & FC_DICT) || STRNCMP(fp->uf_name, "<lambda>", 8) == 0) - return (char_u *)""; // don't show dict and lambda functions + return (char_u *)""; if (STRLEN(fp->uf_name) + 4 >= IOSIZE) return fp->uf_name; // prevents overflow @@ -2837,7 +3172,7 @@ get_user_func_name(expand_T *xp, int idx) if (xp->xp_context != EXPAND_USER_FUNC) { STRCAT(IObuff, "("); - if (!fp->uf_varargs && fp->uf_args.ga_len == 0) + if (!has_varargs(fp) && fp->uf_args.ga_len == 0) STRCAT(IObuff, ")"); } return IObuff; @@ -2876,7 +3211,7 @@ ex_delfunction(exarg_T *eap) *p = NUL; if (!eap->skip) - fp = find_func(name); + fp = find_func(name, NULL); vim_free(name); if (!eap->skip) @@ -2931,7 +3266,7 @@ func_unref(char_u *name) if (name == NULL || !func_name_refcount(name)) return; - fp = find_func(name); + fp = find_func(name, NULL); if (fp == NULL && isdigit(*name)) { #ifdef EXITFREE @@ -2974,7 +3309,7 @@ func_ref(char_u *name) if (name == NULL || !func_name_refcount(name)) return; - fp = find_func(name); + fp = find_func(name, NULL); if (fp != NULL) ++fp->uf_refcount; else if (isdigit(*name)) @@ -3119,7 +3454,7 @@ ex_call(exarg_T *eap) if (*startarg != '(') { - semsg(_(e_missingparen), eap->arg); + semsg(_(e_missing_paren), eap->arg); goto end; } @@ -3444,7 +3779,7 @@ make_partial(dict_T *selfdict_in, typval_T *rettv) : rettv->vval.v_partial->pt_name; // Translate "s:func" to the stored function name. fname = fname_trans_sid(fname, fname_buf, &tofree, &error); - fp = find_func(fname); + fp = find_func(fname, NULL); vim_free(tofree); } @@ -3610,7 +3945,7 @@ get_funccal(void) hashtab_T * get_funccal_local_ht() { - if (current_funccal == NULL) + if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0) return NULL; return &get_funccal()->l_vars.dv_hashtab; } @@ -3622,7 +3957,7 @@ get_funccal_local_ht() dictitem_T * get_funccal_local_var() { - if (current_funccal == NULL) + if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0) return NULL; return &get_funccal()->l_vars_var; } @@ -3634,7 +3969,7 @@ get_funccal_local_var() hashtab_T * get_funccal_args_ht() { - if (current_funccal == NULL) + if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0) return NULL; return &get_funccal()->l_avars.dv_hashtab; } @@ -3646,7 +3981,7 @@ get_funccal_args_ht() dictitem_T * get_funccal_args_var() { - if (current_funccal == NULL) + if (current_funccal == NULL || current_funccal->l_vars.dv_refcount == 0) return NULL; return &get_funccal()->l_avars_var; } @@ -3657,7 +3992,7 @@ get_funccal_args_var() void list_func_vars(int *first) { - if (current_funccal != NULL) + if (current_funccal != NULL && current_funccal->l_vars.dv_refcount > 0) list_hashtable_vars(¤t_funccal->l_vars.dv_hashtab, "l:", FALSE, first); } @@ -3866,7 +4201,7 @@ set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID) if (fp_in == NULL) { fname = fname_trans_sid(name, fname_buf, &tofree, &error); - fp = find_func(fname); + fp = find_func(fname, NULL); } if (fp != NULL) { |