# HG changeset patch # User Christian Brabandt # Date 1452198605 -3600 # Node ID 37e061ec063c2234436b1490b8612cbf444ba0a7 # Parent d9ce9088911e96b75055041df5c3ea2469dcd74f commit https://github.com/vim/vim/commit/75bdf6aa30a5c99d67c42886cf7a4a000bbaa422 Author: Bram Moolenaar Date: Thu Jan 7 21:25:08 2016 +0100 patch 7.4.1058 Problem: It is not possible to test code that is only reached when memory allocation fails. Solution: Add the alloc_fail() function. Try it out with :vimgrep. diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1739,6 +1739,8 @@ USAGE RESULT DESCRIPTION ~ abs( {expr}) Float or Number absolute value of {expr} acos( {expr}) Float arc cosine of {expr} add( {list}, {item}) List append {item} to |List| {list} +alloc_fail( {countdown}, {when}, {repeat}) + nothing make memory allocation fail and( {expr}, {expr}) Number bitwise AND append( {lnum}, {string}) Number append {string} below line {lnum} append( {lnum}, {list}) Number append lines {list} below line {lnum} @@ -2118,6 +2120,13 @@ add({list}, {expr}) *add()* Use |insert()| to add an item at another position. +alloc_fail({id}, {countdown}, {repeat}) *alloc_fail()* + This is for testing: If the memory allocation with {id} is + called, then decrement {countdown}, and when it reaches zero + let memory allocation fail {repeat} times. When {repeat} is + smaller than one it fails one time. + + and({expr}, {expr}) *and()* Bitwise AND on the two arguments. The arguments are converted to a number. A List, Dict or Float argument causes an error. diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -467,6 +467,7 @@ static void f_abs __ARGS((typval_T *argv static void f_acos __ARGS((typval_T *argvars, typval_T *rettv)); #endif static void f_add __ARGS((typval_T *argvars, typval_T *rettv)); +static void f_alloc_fail __ARGS((typval_T *argvars, typval_T *rettv)); static void f_and __ARGS((typval_T *argvars, typval_T *rettv)); static void f_append __ARGS((typval_T *argvars, typval_T *rettv)); static void f_argc __ARGS((typval_T *argvars, typval_T *rettv)); @@ -8071,6 +8072,7 @@ static struct fst {"acos", 1, 1, f_acos}, /* WJMc */ #endif {"add", 2, 2, f_add}, + {"alloc_fail", 3, 3, f_alloc_fail}, {"and", 2, 2, f_and}, {"append", 2, 2, f_append}, {"argc", 0, 0, f_argc}, @@ -8984,6 +8986,28 @@ f_add(argvars, rettv) } /* + * "alloc_fail(id, countdown, repeat)" function + */ + static void +f_alloc_fail(argvars, rettv) + typval_T *argvars; + typval_T *rettv UNUSED; +{ + if (argvars[0].v_type != VAR_NUMBER + || argvars[0].vval.v_number <= 0 + || argvars[1].v_type != VAR_NUMBER + || argvars[1].vval.v_number < 0 + || argvars[2].v_type != VAR_NUMBER) + EMSG(_(e_invarg)); + else + { + alloc_fail_id = argvars[0].vval.v_number; + alloc_fail_countdown = argvars[1].vval.v_number; + alloc_fail_repeat = argvars[2].vval.v_number; + } +} + +/* * "and(expr, expr)" function */ static void diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1619,6 +1619,15 @@ EXTERN FILE *time_fd INIT(= NULL); /* w EXTERN int ignored; EXTERN char *ignoredp; +#ifdef FEAT_EVAL +/* set by alloc_fail(): ID */ +EXTERN int alloc_fail_id INIT(= 0); +/* set by alloc_fail(), when zero alloc() returns NULL */ +EXTERN int alloc_fail_countdown INIT(= -1); +/* set by alloc_fail(), number of times alloc() returns NULL */ +EXTERN int alloc_fail_repeat INIT(= 0); +#endif + /* * Optional Farsi support. Include it here, so EXTERN and INIT are defined. */ diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -797,6 +797,21 @@ vim_mem_profile_dump() #endif /* MEM_PROFILE */ +#ifdef FEAT_EVAL + static int +alloc_does_fail() +{ + if (alloc_fail_countdown == 0) + { + if (--alloc_fail_repeat <= 0) + alloc_fail_id = 0; + return TRUE; + } + --alloc_fail_countdown; + return FALSE; +} +#endif + /* * Some memory is reserved for error messages and for being able to * call mf_release_all(), which needs some memory for mf_trans_add(). @@ -821,6 +836,22 @@ alloc(size) } /* + * alloc() with an ID for alloc_fail(). + * LAST_ID_USED: 5 + */ + char_u * +alloc_id(size, id) + unsigned size; + int id; +{ +#ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail()) + return NULL; +#endif + return (lalloc((long_u)size, TRUE)); +} + +/* * Allocate memory and set all bytes to zero. */ char_u * @@ -968,6 +999,23 @@ theend: return p; } +/* + * lalloc() with an ID for alloc_fail(). + * See LAST_ID_USED above. + */ + char_u * +lalloc_id(size, message, id) + long_u size; + int message; + int id; +{ +#ifdef FEAT_EVAL + if (alloc_fail_id == id && alloc_does_fail()) + return NULL; +#endif + return (lalloc((long_u)size, message)); +} + #if defined(MEM_PROFILE) || defined(PROTO) /* * realloc() with memory profiling. diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -20,10 +20,12 @@ void adjust_cursor_col __ARGS((void)); int leftcol_changed __ARGS((void)); void vim_mem_profile_dump __ARGS((void)); char_u *alloc __ARGS((unsigned size)); +char_u *alloc_id __ARGS((unsigned size, int id)); char_u *alloc_clear __ARGS((unsigned size)); char_u *alloc_check __ARGS((unsigned size)); char_u *lalloc_clear __ARGS((long_u size, int message)); char_u *lalloc __ARGS((long_u size, int message)); +char_u *lalloc_id __ARGS((long_u size, int message, int id)); void *mem_realloc __ARGS((void *ptr, size_t size)); void do_outofmem_msg __ARGS((long_u size)); void free_all_mem __ARGS((void)); diff --git a/src/quickfix.c b/src/quickfix.c --- a/src/quickfix.c +++ b/src/quickfix.c @@ -253,9 +253,9 @@ qf_init_ext(qi, efile, buf, tv, errorfor {'s', ".\\+"} }; - namebuf = alloc(CMDBUFFSIZE + 1); - errmsg = alloc(CMDBUFFSIZE + 1); - pattern = alloc(CMDBUFFSIZE + 1); + namebuf = alloc_id(CMDBUFFSIZE + 1, 3); + errmsg = alloc_id(CMDBUFFSIZE + 1, 4); + pattern = alloc_id(CMDBUFFSIZE + 1, 5); if (namebuf == NULL || errmsg == NULL || pattern == NULL) goto qf_init_end; @@ -3465,8 +3465,8 @@ ex_vimgrep(eap) goto theend; } - dirname_start = alloc(MAXPATHL); - dirname_now = alloc(MAXPATHL); + dirname_start = alloc_id(MAXPATHL, 1); + dirname_now = alloc_id(MAXPATHL, 2); if (dirname_start == NULL || dirname_now == NULL) goto theend; diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim --- a/src/testdir/test_quickfix.vim +++ b/src/testdir/test_quickfix.vim @@ -273,3 +273,42 @@ function Test_cbuffer() call XbufferTests('l') endfunction +function Test_nomem() + call alloc_fail(1, 0, 0) + try + vimgrep vim runtest.vim + catch + call assert_true(v:exception =~ 'E342') + endtry + + call alloc_fail(2, 0, 0) + try + vimgrep vim runtest.vim + catch + call assert_true(v:exception =~ 'E342') + endtry + + call alloc_fail(3, 0, 0) + try + cfile runtest.vim + catch + call assert_true(v:exception =~ 'E342') + endtry + + call alloc_fail(4, 0, 0) + try + cfile runtest.vim + catch + call assert_true(v:exception =~ 'E342') + endtry + + call alloc_fail(5, 0, 0) + try + cfile runtest.vim + catch + call assert_true(v:exception =~ 'E342') + endtry + +endfunc + + diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -742,6 +742,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1058, +/**/ 1057, /**/ 1056,