1
0
forked from aniani/vim

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 #12005)
            Handle error from calling a user function better.
This commit is contained in:
Bram Moolenaar
2023-02-18 14:42:44 +00:00
parent 9de960ace0
commit 0917e86763
10 changed files with 94 additions and 48 deletions

View File

@@ -3056,7 +3056,7 @@ internal_func_is_map(int idx)
int int
check_internal_func(int idx, int argcount) check_internal_func(int idx, int argcount)
{ {
int res; funcerror_T res;
char *name; char *name;
if (argcount < global_functions[idx].f_min_argc) if (argcount < global_functions[idx].f_min_argc)
@@ -3074,7 +3074,7 @@ check_internal_func(int idx, int argcount)
return -1; return -1;
} }
int funcerror_T
call_internal_func( call_internal_func(
char_u *name, char_u *name,
int argcount, int argcount,
@@ -3107,7 +3107,7 @@ call_internal_func_by_idx(
/* /*
* Invoke a method for base->method(). * Invoke a method for base->method().
*/ */
int funcerror_T
call_internal_method( call_internal_method(
char_u *name, char_u *name,
int argcount, int argcount,

View File

@@ -2776,11 +2776,11 @@ luaV_call_lua_func(
if (lua_pcall(funcstate->L, luaargcount, 1, 0)) if (lua_pcall(funcstate->L, luaargcount, 1, 0))
{ {
luaV_emsg(funcstate->L); luaV_emsg(funcstate->L);
return FCERR_OTHER; return (int)FCERR_OTHER;
} }
luaV_checktypval(funcstate->L, -1, rettv, "get return value"); luaV_checktypval(funcstate->L, -1, rettv, "get return value");
return FCERR_NONE; return (int)FCERR_NONE;
} }
/* /*

View File

@@ -10,9 +10,9 @@ void internal_func_get_argcount(int idx, int *argcount, int *min_argcount);
type_T *internal_func_ret_type(int idx, int argcount, type2_T *argtypes, type_T **decl_type, garray_T *type_gap); 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 internal_func_is_map(int idx);
int check_internal_func(int idx, int argcount); 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); 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); int non_zero_arg(typval_T *argvars);
buf_T *get_buf_arg(typval_T *arg); buf_T *get_buf_arg(typval_T *arg);
win_T *get_optional_window(typval_T *argvars, int idx); win_T *get_optional_window(typval_T *argvars, int idx);

View File

@@ -9,7 +9,7 @@ char_u *deref_func_name(char_u *name, int *lenp, partial_T **partialp, type_T **
void emsg_funcname(char *ermsg, char_u *name); 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_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); 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); 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_even_dead(char_u *name, int flags);
ufunc_T *find_func(char_u *name, int is_global); ufunc_T *find_func(char_u *name, int is_global);
@@ -24,8 +24,8 @@ int funcdepth_get(void);
void funcdepth_restore(int depth); void funcdepth_restore(int depth);
funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv); funccall_T *create_funccal(ufunc_T *fp, typval_T *rettv);
void remove_funccal(void); void remove_funccal(void);
int check_user_func_argcount(ufunc_T *fp, int argcount); funcerror_T 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 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 save_funccal(funccal_entry_T *entry);
void restore_funccal(void); void restore_funccal(void);
funccall_T *get_current_funccal(void); funccall_T *get_current_funccal(void);
@@ -37,7 +37,7 @@ int func_call(char_u *name, typval_T *args, partial_T *partial, dict_T *selfdict
int get_callback_depth(void); int get_callback_depth(void);
int call_callback(callback_T *callback, int len, typval_T *rettv, int argcount, typval_T *argvars); 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); 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_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); int call_simple_func(char_u *funcname, int len, typval_T *rettv);
char_u *printable_func_name(ufunc_T *fp); char_u *printable_func_name(ufunc_T *fp);

View File

@@ -195,6 +195,26 @@ def Test_object_not_set()
echo db[state.value] echo db[state.value]
END END
v9.CheckScriptFailure(lines, 'E1360:') 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<Background> but got object<Unknown>')
enddef enddef
def Test_class_member_initializer() def Test_class_member_initializer()

View File

@@ -1979,7 +1979,11 @@ eval_fname_sid(char_u *p)
* and set "tofree". * and set "tofree".
*/ */
char_u * 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; int llen;
char_u *fname; char_u *fname;
@@ -2716,7 +2720,7 @@ remove_funccal()
/* /*
* Call a user function. * Call a user function.
*/ */
static void static funcerror_T
call_user_func( call_user_func(
ufunc_T *fp, // pointer to function ufunc_T *fp, // pointer to function
int argcount, // nr of args int argcount, // nr of args
@@ -2731,6 +2735,7 @@ call_user_func(
int save_sticky_cmdmod_flags = sticky_cmdmod_flags; int save_sticky_cmdmod_flags = sticky_cmdmod_flags;
funccall_T *fc; funccall_T *fc;
int save_did_emsg; int save_did_emsg;
funcerror_T retval = FCERR_NONE;
int default_arg_err = FALSE; int default_arg_err = FALSE;
dictitem_T *v; dictitem_T *v;
int fixvar_idx = 0; // index in fc_fixvar[] int fixvar_idx = 0; // index in fc_fixvar[]
@@ -2755,14 +2760,14 @@ call_user_func(
{ {
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
return; return FCERR_FAILED;
} }
line_breakcheck(); // check for CTRL-C hit line_breakcheck(); // check for CTRL-C hit
fc = create_funccal(fp, rettv); fc = create_funccal(fp, rettv);
if (fc == NULL) if (fc == NULL)
return; return FCERR_OTHER;
fc->fc_level = ex_nesting_level; fc->fc_level = ex_nesting_level;
// Check if this function has a breakpoint. // Check if this function has a breakpoint.
fc->fc_breakpoint = dbg_find_breakpoint(FALSE, fp->uf_name, (linenr_T)0); 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); profile_may_start_func(&profile_info, fp, caller);
#endif #endif
sticky_cmdmod_flags = 0; sticky_cmdmod_flags = 0;
call_def_function(fp, argcount, argvars, 0, if (call_def_function(fp, argcount, argvars, 0,
funcexe->fe_partial, funcexe->fe_object, fc, rettv); funcexe->fe_partial, funcexe->fe_object, fc, rettv) == FAIL)
retval = FCERR_FAILED;
funcdepth_decrement(); funcdepth_decrement();
#ifdef FEAT_PROFILE #ifdef FEAT_PROFILE
if (do_profiling == PROF_YES && (fp->uf_profiling if (do_profiling == PROF_YES && (fp->uf_profiling
@@ -2791,7 +2797,7 @@ call_user_func(
#endif #endif
remove_funccal(); remove_funccal();
sticky_cmdmod_flags = save_sticky_cmdmod_flags; sticky_cmdmod_flags = save_sticky_cmdmod_flags;
return; return retval;
} }
islambda = fp->uf_flags & FC_LAMBDA; islambda = fp->uf_flags & FC_LAMBDA;
@@ -3024,7 +3030,10 @@ call_user_func(
did_emsg = FALSE; did_emsg = FALSE;
if (default_arg_err && (fp->uf_flags & FC_ABORT)) if (default_arg_err && (fp->uf_flags & FC_ABORT))
{
did_emsg = TRUE; did_emsg = TRUE;
retval = FCERR_FAILED;
}
else if (islambda) else if (islambda)
{ {
char_u *p = *(char_u **)fp->uf_lines.ga_data + 7; char_u *p = *(char_u **)fp->uf_lines.ga_data + 7;
@@ -3051,6 +3060,7 @@ call_user_func(
clear_tv(rettv); clear_tv(rettv);
rettv->v_type = VAR_NUMBER; rettv->v_type = VAR_NUMBER;
rettv->vval.v_number = -1; rettv->vval.v_number = -1;
retval = FCERR_FAILED;
} }
#ifdef FEAT_PROFILE #ifdef FEAT_PROFILE
@@ -3134,13 +3144,15 @@ call_user_func(
for (i = 0; i < tv_to_free_len; ++i) for (i = 0; i < tv_to_free_len; ++i)
clear_tv(tv_to_free[i]); clear_tv(tv_to_free[i]);
cleanup_function_call(fc); cleanup_function_call(fc);
return retval;
} }
/* /*
* Check the argument count for user function "fp". * Check the argument count for user function "fp".
* Return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise. * Return FCERR_UNKNOWN if OK, FCERR_TOOFEW or FCERR_TOOMANY otherwise.
*/ */
int funcerror_T
check_user_func_argcount(ufunc_T *fp, int argcount) check_user_func_argcount(ufunc_T *fp, int argcount)
{ {
int regular_args = fp->uf_args.ga_len; int regular_args = fp->uf_args.ga_len;
@@ -3155,7 +3167,7 @@ check_user_func_argcount(ufunc_T *fp, int argcount)
/* /*
* Call a user function after checking the arguments. * Call a user function after checking the arguments.
*/ */
int funcerror_T
call_user_func_check( call_user_func_check(
ufunc_T *fp, ufunc_T *fp,
int argcount, int argcount,
@@ -3164,7 +3176,7 @@ call_user_func_check(
funcexe_T *funcexe, funcexe_T *funcexe,
dict_T *selfdict) dict_T *selfdict)
{ {
int error; funcerror_T error = FCERR_NONE;
#ifdef FEAT_LUA #ifdef FEAT_LUA
if (fp->uf_flags & FC_CFUNC) if (fp->uf_flags & FC_CFUNC)
@@ -3180,8 +3192,11 @@ call_user_func_check(
error = check_user_func_argcount(fp, argcount); error = check_user_func_argcount(fp, argcount);
if (error != FCERR_UNKNOWN) if (error != FCERR_UNKNOWN)
return error; return error;
if ((fp->uf_flags & FC_DICT) && selfdict == NULL) if ((fp->uf_flags & FC_DICT) && selfdict == NULL)
{
error = FCERR_DICT; error = FCERR_DICT;
}
else else
{ {
int did_save_redo = FALSE; int did_save_redo = FALSE;
@@ -3199,7 +3214,7 @@ call_user_func_check(
did_save_redo = TRUE; did_save_redo = TRUE;
} }
++fp->uf_calls; ++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); (fp->uf_flags & FC_DICT) ? selfdict : NULL);
if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0) if (--fp->uf_calls <= 0 && fp->uf_refcount <= 0)
// Function was unreferenced while being used, free it now. // Function was unreferenced while being used, free it now.
@@ -3207,8 +3222,8 @@ call_user_func_check(
if (did_save_redo) if (did_save_redo)
restoreRedobuff(&save_redo); restoreRedobuff(&save_redo);
restore_search_patterns(); restore_search_patterns();
error = FCERR_NONE;
} }
return error; return error;
} }
@@ -3542,7 +3557,7 @@ call_callback_retnr(
* Nothing if "error" is FCERR_NONE. * Nothing if "error" is FCERR_NONE.
*/ */
void 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) switch (error)
{ {
@@ -3571,6 +3586,12 @@ user_func_error(int error, char_u *name, int found_var)
emsg_funcname(e_calling_dict_function_without_dictionary_str, emsg_funcname(e_calling_dict_function_without_dictionary_str,
name); name);
break; 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 funcexe_T *funcexe) // more arguments
{ {
int ret = FAIL; int ret = FAIL;
int error = FCERR_NONE; funcerror_T error = FCERR_NONE;
int i; int i;
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
@@ -3823,7 +3844,7 @@ call_simple_func(
typval_T *rettv) // return value goes here typval_T *rettv) // return value goes here
{ {
int ret = FAIL; int ret = FAIL;
int error = FCERR_NONE; funcerror_T error = FCERR_NONE;
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL; char_u *tofree = NULL;
char_u *name; char_u *name;
@@ -5973,8 +5994,7 @@ ex_defer_inner(
// we tolerate an unknown function here, it might be defined later // we tolerate an unknown function here, it might be defined later
if (ufunc != NULL) if (ufunc != NULL)
{ {
int error = check_user_func_argcount(ufunc, argcount); funcerror_T error = check_user_func_argcount(ufunc, argcount);
if (error != FCERR_UNKNOWN) if (error != FCERR_UNKNOWN)
{ {
user_func_error(error, name, FALSE); user_func_error(error, name, FALSE);
@@ -6449,7 +6469,6 @@ make_partial(dict_T *selfdict_in, typval_T *rettv)
char_u *fname; char_u *fname;
ufunc_T *fp = NULL; ufunc_T *fp = NULL;
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
int error;
dict_T *selfdict = selfdict_in; dict_T *selfdict = selfdict_in;
if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial != NULL if (rettv->v_type == VAR_PARTIAL && rettv->vval.v_partial != NULL
@@ -6470,6 +6489,7 @@ make_partial(dict_T *selfdict_in, typval_T *rettv)
else else
{ {
char_u *tofree = NULL; char_u *tofree = NULL;
funcerror_T error;
// Translate "s:func" to the stored function name. // Translate "s:func" to the stored function name.
fname = fname_trans_sid(fname, fname_buf, &tofree, &error); fname = fname_trans_sid(fname, fname_buf, &tofree, &error);
@@ -6881,7 +6901,7 @@ set_ref_in_func(char_u *name, ufunc_T *fp_in, int copyID)
{ {
ufunc_T *fp = fp_in; ufunc_T *fp = fp_in;
funccall_T *fc; funccall_T *fc;
int error = FCERR_NONE; funcerror_T error = FCERR_NONE;
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL; char_u *tofree = NULL;
char_u *fname; char_u *fname;

View File

@@ -695,6 +695,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
1320,
/**/ /**/
1319, 1319,
/**/ /**/

View File

@@ -2270,6 +2270,20 @@ typedef enum {
KEYPROTOCOL_FAIL KEYPROTOCOL_FAIL
} keyprot_T; } 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. // Flags for assignment functions.
#define ASSIGN_VAR 0 // ":var" (nothing special) #define ASSIGN_VAR 0 // ":var" (nothing special)
#define ASSIGN_FINAL 0x01 // ":final" #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 #define DO_NOT_FREE_CNT 99999 // refcount for dict or list that should not
// be freed. // 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() // fixed buffer length for fname_trans_sid()
#define FLEN_FIXED 40 #define FLEN_FIXED 40

View File

@@ -1283,7 +1283,7 @@ call_ufunc(
{ {
typval_T argvars[MAX_FUNC_ARGS]; typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe; funcexe_T funcexe;
int error; funcerror_T error;
int idx; int idx;
int did_emsg_before = did_emsg; int did_emsg_before = did_emsg;
compiletype_T compile_type = get_compile_type(ufunc); compiletype_T compile_type = get_compile_type(ufunc);
@@ -1466,7 +1466,7 @@ call_partial(
{ {
char_u fname_buf[FLEN_FIXED + 1]; char_u fname_buf[FLEN_FIXED + 1];
char_u *tofree = NULL; char_u *tofree = NULL;
int error = FCERR_NONE; funcerror_T error = FCERR_NONE;
char_u *fname; char_u *fname;
// May need to translate <SNR>123_ to K_SNR. // May need to translate <SNR>123_ to K_SNR.

View File

@@ -1659,7 +1659,8 @@ type_name(type_T *type, char **tofree)
if (type->tt_type == VAR_OBJECT || type->tt_type == VAR_CLASS) 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; size_t len = STRLEN(name) + STRLEN(class_name) + 3;
*tofree = alloc(len); *tofree = alloc(len);
if (*tofree != NULL) if (*tofree != NULL)