# HG changeset patch # User Bram Moolenaar # Date 1676731504 -3600 # Node ID 16025ef158bf0ab9738f719b7b0c7e72b546e9f0 # Parent eed95e0f92ff5d8259dff92c5f05123ba0ebe2de patch 9.0.1320: checking the type of a null object causes a crash Commit: https://github.com/vim/vim/commit/0917e867632199883c07c2d7534f7091b1d12607 Author: Bram Moolenaar Date: Sat Feb 18 14:42:44 2023 +0000 patch 9.0.1320: checking the type of a null object causes a crash Problem: Checking the type of a null object causes a crash. Solution: Don't try to get the class of a null object. (closes https://github.com/vim/vim/issues/12005) Handle error from calling a user function better. diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -3056,8 +3056,8 @@ internal_func_is_map(int idx) int check_internal_func(int idx, int argcount) { - int res; - char *name; + funcerror_T res; + char *name; if (argcount < global_functions[idx].f_min_argc) res = FCERR_TOOFEW; @@ -3074,7 +3074,7 @@ check_internal_func(int idx, int argcoun return -1; } - int + funcerror_T call_internal_func( char_u *name, int argcount, @@ -3107,7 +3107,7 @@ call_internal_func_by_idx( /* * Invoke a method for base->method(). */ - int + funcerror_T call_internal_method( char_u *name, int argcount, diff --git a/src/if_lua.c b/src/if_lua.c --- a/src/if_lua.c +++ b/src/if_lua.c @@ -2776,11 +2776,11 @@ luaV_call_lua_func( if (lua_pcall(funcstate->L, luaargcount, 1, 0)) { luaV_emsg(funcstate->L); - return FCERR_OTHER; + return (int)FCERR_OTHER; } luaV_checktypval(funcstate->L, -1, rettv, "get return value"); - return FCERR_NONE; + return (int)FCERR_NONE; } /* diff --git a/src/proto/evalfunc.pro b/src/proto/evalfunc.pro --- a/src/proto/evalfunc.pro +++ b/src/proto/evalfunc.pro @@ -10,9 +10,9 @@ void internal_func_get_argcount(int idx, type_T *internal_func_ret_type(int idx, int argcount, type2_T *argtypes, type_T **decl_type, garray_T *type_gap); int internal_func_is_map(int idx); int check_internal_func(int idx, int argcount); -int call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv); +funcerror_T call_internal_func(char_u *name, int argcount, typval_T *argvars, typval_T *rettv); void call_internal_func_by_idx(int idx, typval_T *argvars, typval_T *rettv); -int call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv); +funcerror_T call_internal_method(char_u *name, int argcount, typval_T *argvars, typval_T *rettv, typval_T *basetv); int non_zero_arg(typval_T *argvars); buf_T *get_buf_arg(typval_T *arg); win_T *get_optional_window(typval_T *argvars, int idx); diff --git a/src/proto/userfunc.pro b/src/proto/userfunc.pro --- a/src/proto/userfunc.pro +++ b/src/proto/userfunc.pro @@ -9,7 +9,7 @@ char_u *deref_func_name(char_u *name, in void emsg_funcname(char *ermsg, char_u *name); int get_func_arguments(char_u **arg, evalarg_T *evalarg, int partial_argc, typval_T *argvars, int *argcount); int get_func_tv(char_u *name, int len, typval_T *rettv, char_u **arg, evalarg_T *evalarg, funcexe_T *funcexe); -char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error); +char_u *fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, funcerror_T *error); void func_name_with_sid(char_u *name, int sid, char_u *buffer); ufunc_T *find_func_even_dead(char_u *name, int flags); ufunc_T *find_func(char_u *name, int is_global); @@ -24,8 +24,8 @@ int funcdepth_get(void); void funcdepth_restore(int depth); funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv); void remove_funccal(void); -int check_user_func_argcount(ufunc_T *fp, int argcount); -int call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); +funcerror_T check_user_func_argcount(ufunc_T *fp, int argcount); +funcerror_T call_user_func_check(ufunc_T *fp, int argcount, typval_T *argvars, typval_T *rettv, funcexe_T *funcexe, dict_T *selfdict); void save_funccal(funccal_entry_T *entry); void restore_funccal(void); funccall_T *get_current_funccal(void); @@ -37,7 +37,7 @@ int func_call(char_u *name, typval_T *ar int get_callback_depth(void); int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars); varnumber_T call_callback_retnr(callback_T *callback, int argcount, typval_T *argvars); -void user_func_error(int error, char_u *name, int found_var); +void user_func_error(funcerror_T error, char_u *name, int found_var); int call_func(char_u *funcname, int len, typval_T *rettv, int argcount_in, typval_T *argvars_in, funcexe_T *funcexe); int call_simple_func(char_u *funcname, int len, typval_T *rettv); char_u *printable_func_name(ufunc_T *fp); diff --git a/src/testdir/test_vim9_class.vim b/src/testdir/test_vim9_class.vim --- a/src/testdir/test_vim9_class.vim +++ b/src/testdir/test_vim9_class.vim @@ -195,6 +195,26 @@ def Test_object_not_set() echo db[state.value] END v9.CheckScriptFailure(lines, 'E1360:') + + lines =<< trim END + vim9script + + class Background + this.background = 'dark' + endclass + + class Colorscheme + this._bg: Background + + def GetBackground(): string + return this._bg.background + enddef + endclass + + var bg: Background # UNINITIALIZED + echo Colorscheme.new(bg).GetBackground() + END + v9.CheckScriptFailure(lines, 'E1012: Type mismatch; expected object but got object') enddef def Test_class_member_initializer() diff --git a/src/userfunc.c b/src/userfunc.c --- a/src/userfunc.c +++ b/src/userfunc.c @@ -1979,7 +1979,11 @@ eval_fname_sid(char_u *p) * and set "tofree". */ char_u * -fname_trans_sid(char_u *name, char_u *fname_buf, char_u **tofree, int *error) +fname_trans_sid( + char_u *name, + char_u *fname_buf, + char_u **tofree, + funcerror_T *error) { int llen; char_u *fname; @@ -2716,7 +2720,7 @@ remove_funccal() /* * Call a user function. */ - static void + static funcerror_T call_user_func( ufunc_T *fp, // pointer to function int argcount, // nr of args @@ -2731,6 +2735,7 @@ call_user_func( int save_sticky_cmdmod_flags = sticky_cmdmod_flags; funccall_T *fc; int save_did_emsg; + funcerror_T retval = FCERR_NONE; int default_arg_err = FALSE; dictitem_T *v; int fixvar_idx = 0; // index in fc_fixvar[] @@ -2755,14 +2760,14 @@ call_user_func( { rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; - return; + return FCERR_FAILED; } line_breakcheck(); // check for CTRL-C hit fc = create_funccal(fp, rettv); if (fc == NULL) - return; + return FCERR_OTHER; fc->fc_level = ex_nesting_level; // Check if this function has a breakpoint. fc->fc_breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0); @@ -2781,8 +2786,9 @@ call_user_func( profile_may_start_func(&profile_info, fp, caller); #endif sticky_cmdmod_flags = 0; - call_def_function(fp, argcount, argvars, 0, - funcexe->fe_partial, funcexe->fe_object, fc, rettv); + if (call_def_function(fp, argcount, argvars, 0, + funcexe->fe_partial, funcexe->fe_object, fc, rettv) == FAIL) + retval = FCERR_FAILED; funcdepth_decrement(); #ifdef FEAT_PROFILE if (do_profiling == PROF_YES && (fp->uf_profiling @@ -2791,7 +2797,7 @@ call_user_func( #endif remove_funccal(); sticky_cmdmod_flags = save_sticky_cmdmod_flags; - return; + return retval; } islambda = fp->uf_flags & FC_LAMBDA; @@ -3024,7 +3030,10 @@ call_user_func( did_emsg = FALSE; if (default_arg_err && (fp->uf_flags & FC_ABORT)) + { did_emsg = TRUE; + retval = FCERR_FAILED; + } else if (islambda) { char_u *p = *(char_u **)fp->uf_lines.ga_data + 7; @@ -3051,6 +3060,7 @@ call_user_func( clear_tv(rettv); rettv->v_type = VAR_NUMBER; rettv->vval.v_number = -1; + retval = FCERR_FAILED; } #ifdef FEAT_PROFILE @@ -3134,13 +3144,15 @@ call_user_func( for (i = 0; i < tv_to_free_len; ++i) clear_tv(tv_to_free[i]); cleanup_function_call(fc); + + return retval; } /* * Check the argument count for user function "fp". * Return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise. */ - int + funcerror_T check_user_func_argcount(ufunc_T *fp, int argcount) { int regular_args = fp->uf_args.ga_len; @@ -3155,7 +3167,7 @@ check_user_func_argcount(ufunc_T *fp, in /* * Call a user function after checking the arguments. */ - int + funcerror_T call_user_func_check( ufunc_T *fp, int argcount, @@ -3164,7 +3176,7 @@ call_user_func_check( funcexe_T *funcexe, dict_T *selfdict) { - int error; + funcerror_T error = FCERR_NONE; #ifdef FEAT_LUA if (fp->uf_flags & FC_CFUNC) @@ -3180,8 +3192,11 @@ call_user_func_check( error = check_user_func_argcount(fp, argcount); if (error != FCERR_UNKNOWN) return error; + if ((fp->uf_flags & FC_DICT) && selfdict == NULL) + { error = FCERR_DICT; + } else { int did_save_redo = FALSE; @@ -3199,7 +3214,7 @@ call_user_func_check( did_save_redo = TRUE; } ++fp->uf_calls; - call_user_func(fp, argcount, argvars, rettv, funcexe, + error = call_user_func(fp, argcount, argvars, rettv, funcexe, (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. @@ -3207,8 +3222,8 @@ call_user_func_check( if (did_save_redo) restoreRedobuff(&save_redo); restore_search_patterns(); - error = FCERR_NONE; - } + } + return error; } @@ -3542,7 +3557,7 @@ call_callback_retnr( * Nothing if "error" is FCERR_NONE. */ void -user_func_error(int error, char_u *name, int found_var) +user_func_error(funcerror_T error, char_u *name, int found_var) { switch (error) { @@ -3571,6 +3586,12 @@ user_func_error(int error, char_u *name, emsg_funcname(e_calling_dict_function_without_dictionary_str, name); break; + case FCERR_OTHER: + case FCERR_FAILED: + // assume the error message was already given + break; + case FCERR_NONE: + break; } } @@ -3591,7 +3612,7 @@ call_func( funcexe_T *funcexe) // more arguments { int ret = FAIL; - int error = FCERR_NONE; + funcerror_T error = FCERR_NONE; int i; ufunc_T *fp = NULL; char_u fname_buf[FLEN_FIXED + 1]; @@ -3823,7 +3844,7 @@ call_simple_func( typval_T *rettv) // return value goes here { int ret = FAIL; - int error = FCERR_NONE; + funcerror_T error = FCERR_NONE; char_u fname_buf[FLEN_FIXED + 1]; char_u *tofree = NULL; char_u *name; @@ -5973,8 +5994,7 @@ ex_defer_inner( // we tolerate an unknown function here, it might be defined later if (ufunc != NULL) { - int error = check_user_func_argcount(ufunc, argcount); - + funcerror_T error = check_user_func_argcount(ufunc, argcount); if (error != FCERR_UNKNOWN) { user_func_error(error, name, FALSE); @@ -6449,7 +6469,6 @@ make_partial(dict_T *selfdict_in, typval char_u *fname; ufunc_T *fp = NULL; char_u fname_buf[FLEN_FIXED + 1]; - int error; dict_T *selfdict = selfdict_in; if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial != NULL @@ -6470,6 +6489,7 @@ make_partial(dict_T *selfdict_in, typval else { char_u *tofree = NULL; + funcerror_T error; // Translate "s:func" to the stored function name. fname = fname_trans_sid(fname, fname_buf, &tofree, &error); @@ -6881,7 +6901,7 @@ set_ref_in_func(char_u *name, ufunc_T *f { ufunc_T *fp = fp_in; funccall_T *fc; - int error = FCERR_NONE; + funcerror_T error = FCERR_NONE; char_u fname_buf[FLEN_FIXED + 1]; char_u *tofree = NULL; char_u *fname; diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -696,6 +696,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1320, +/**/ 1319, /**/ 1318, diff --git a/src/vim.h b/src/vim.h --- a/src/vim.h +++ b/src/vim.h @@ -2270,6 +2270,20 @@ typedef enum { KEYPROTOCOL_FAIL } keyprot_T; +// errors for when calling a function +typedef enum { + FCERR_NONE, // no error + FCERR_UNKNOWN, // unknown function + FCERR_TOOMANY, // too many arguments + FCERR_TOOFEW, // too few arguments + FCERR_SCRIPT, // missing script context + FCERR_DICT, // missing dict + FCERR_OTHER, // another kind of error + FCERR_DELETED, // function was deleted + FCERR_NOTMETHOD, // function cannot be used as a method + FCERR_FAILED, // error while executing the function +} funcerror_T; + // Flags for assignment functions. #define ASSIGN_VAR 0 // ":var" (nothing special) #define ASSIGN_FINAL 0x01 // ":final" @@ -2703,17 +2717,6 @@ typedef enum { #define DO_NOT_FREE_CNT 99999 // refcount for dict or list that should not // be freed. -// errors for when calling a function -#define FCERR_UNKNOWN 0 -#define FCERR_TOOMANY 1 -#define FCERR_TOOFEW 2 -#define FCERR_SCRIPT 3 -#define FCERR_DICT 4 -#define FCERR_NONE 5 -#define FCERR_OTHER 6 -#define FCERR_DELETED 7 -#define FCERR_NOTMETHOD 8 // function cannot be used as a method - // fixed buffer length for fname_trans_sid() #define FLEN_FIXED 40 diff --git a/src/vim9execute.c b/src/vim9execute.c --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -1283,7 +1283,7 @@ call_ufunc( { typval_T argvars[MAX_FUNC_ARGS]; funcexe_T funcexe; - int error; + funcerror_T error; int idx; int did_emsg_before = did_emsg; compiletype_T compile_type = get_compile_type(ufunc); @@ -1464,10 +1464,10 @@ call_partial( name = tv->vval.v_string; if (name != NULL) { - char_u fname_buf[FLEN_FIXED + 1]; - char_u *tofree = NULL; - int error = FCERR_NONE; - char_u *fname; + char_u fname_buf[FLEN_FIXED + 1]; + char_u *tofree = NULL; + funcerror_T error = FCERR_NONE; + char_u *fname; // May need to translate 123_ to K_SNR. fname = fname_trans_sid(name, fname_buf, &tofree, &error); diff --git a/src/vim9type.c b/src/vim9type.c --- a/src/vim9type.c +++ b/src/vim9type.c @@ -1659,7 +1659,8 @@ type_name(type_T *type, char **tofree) if (type->tt_type == VAR_OBJECT || type->tt_type == VAR_CLASS) { - char_u *class_name = ((class_T *)type->tt_member)->class_name; + char_u *class_name = type->tt_member == NULL ? (char_u *)"Unknown" + : ((class_T *)type->tt_member)->class_name; size_t len = STRLEN(name) + STRLEN(class_name) + 3; *tofree = alloc(len); if (*tofree != NULL)