# HG changeset patch # User Bram Moolenaar # Date 1553347805 -3600 # Node ID d13aa9c5a1d16bd2f5fa0f7dac29d8569343ddac # Parent e5e2a222cb79252f940649d8cba91dda1be0e834 patch 8.1.1044: no way to check the reference count of objects commit https://github.com/vim/vim/commit/c3e92c161d6394d126a334011526c02e8c3f655f Author: Bram Moolenaar Date: Sat Mar 23 14:23:07 2019 +0100 patch 8.1.1044: no way to check the reference count of objects Problem: No way to check the reference count of objects. Solution: Add test_refcount(). (Ozaki Kiichi, closes https://github.com/vim/vim/issues/4124) diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2672,6 +2672,7 @@ test_null_partial() Funcref null value test_null_string() String null value for testing test_option_not_set({name}) none reset flag indicating option was set test_override({expr}, {val}) none test with Vim internal overrides +test_refcount({expr}) Number get the reference count of {expr} test_scrollbar({which}, {value}, {dragging}) none scroll in the GUI for testing test_settime({expr}) none set current time for testing @@ -9589,6 +9590,11 @@ test_override({name}, {val}) *test_ov < The value of "starting" is saved. It is restored by: > call test_override('starting', 0) +test_refcount({expr}) *test_refcount()* + Return the reference count of {expr}. When {expr} is of a + type that does not have a reference count, returns -1. Only + to be used for testing. + test_scrollbar({which}, {value}, {dragging}) *test_scrollbar()* Pretend using scrollbar {which} to move it to position {value}. {which} can be: diff --git a/src/evalfunc.c b/src/evalfunc.c --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -428,6 +428,7 @@ static void f_test_autochdir(typval_T *a static void f_test_feedinput(typval_T *argvars, typval_T *rettv); static void f_test_option_not_set(typval_T *argvars, typval_T *rettv); static void f_test_override(typval_T *argvars, typval_T *rettv); +static void f_test_refcount(typval_T *argvars, typval_T *rettv); static void f_test_garbagecollect_now(typval_T *argvars, typval_T *rettv); static void f_test_ignore_error(typval_T *argvars, typval_T *rettv); static void f_test_null_blob(typval_T *argvars, typval_T *rettv); @@ -952,7 +953,7 @@ static struct fst {"test_feedinput", 1, 1, f_test_feedinput}, {"test_garbagecollect_now", 0, 0, f_test_garbagecollect_now}, {"test_ignore_error", 1, 1, f_test_ignore_error}, - {"test_null_blob", 0, 0, f_test_null_blob}, + {"test_null_blob", 0, 0, f_test_null_blob}, #ifdef FEAT_JOB_CHANNEL {"test_null_channel", 0, 0, f_test_null_channel}, #endif @@ -964,7 +965,8 @@ static struct fst {"test_null_partial", 0, 0, f_test_null_partial}, {"test_null_string", 0, 0, f_test_null_string}, {"test_option_not_set", 1, 1, f_test_option_not_set}, - {"test_override", 2, 2, f_test_override}, + {"test_override", 2, 2, f_test_override}, + {"test_refcount", 1, 1, f_test_refcount}, #ifdef FEAT_GUI {"test_scrollbar", 3, 3, f_test_scrollbar}, #endif @@ -13847,6 +13849,67 @@ f_test_override(typval_T *argvars, typva } /* + * "test_refcount({expr})" function + */ + static void +f_test_refcount(typval_T *argvars, typval_T *rettv) +{ + int retval = -1; + + switch (argvars[0].v_type) + { + case VAR_UNKNOWN: + case VAR_NUMBER: + case VAR_FLOAT: + case VAR_SPECIAL: + case VAR_STRING: + break; + case VAR_JOB: +#ifdef FEAT_JOB_CHANNEL + if (argvars[0].vval.v_job != NULL) + retval = argvars[0].vval.v_job->jv_refcount - 1; +#endif + break; + case VAR_CHANNEL: +#ifdef FEAT_JOB_CHANNEL + if (argvars[0].vval.v_channel != NULL) + retval = argvars[0].vval.v_channel->ch_refcount - 1; +#endif + break; + case VAR_FUNC: + if (argvars[0].vval.v_string != NULL) + { + ufunc_T *fp; + + fp = find_func(argvars[0].vval.v_string); + if (fp != NULL) + retval = fp->uf_refcount; + } + break; + case VAR_PARTIAL: + if (argvars[0].vval.v_partial != NULL) + retval = argvars[0].vval.v_partial->pt_refcount - 1; + break; + case VAR_BLOB: + if (argvars[0].vval.v_blob != NULL) + retval = argvars[0].vval.v_blob->bv_refcount - 1; + break; + case VAR_LIST: + if (argvars[0].vval.v_list != NULL) + retval = argvars[0].vval.v_list->lv_refcount - 1; + break; + case VAR_DICT: + if (argvars[0].vval.v_dict != NULL) + retval = argvars[0].vval.v_dict->dv_refcount - 1; + break; + } + + rettv->v_type = VAR_NUMBER; + rettv->vval.v_number = retval; + +} + +/* * "test_garbagecollect_now()" function */ static void diff --git a/src/testdir/test_vimscript.vim b/src/testdir/test_vimscript.vim --- a/src/testdir/test_vimscript.vim +++ b/src/testdir/test_vimscript.vim @@ -1556,6 +1556,115 @@ func Test_compound_assignment_operators( let @/ = '' endfunc +func Test_refcount() + " Immediate values + call assert_equal(-1, test_refcount(1)) + call assert_equal(-1, test_refcount('s')) + call assert_equal(-1, test_refcount(v:true)) + call assert_equal(0, test_refcount([])) + call assert_equal(0, test_refcount({})) + call assert_equal(0, test_refcount(0zff)) + call assert_equal(0, test_refcount({-> line('.')})) + if has('float') + call assert_equal(-1, test_refcount(0.1)) + endif + if has('job') + call assert_equal(0, test_refcount(job_start([&shell, &shellcmdflag, 'echo .']))) + endif + + " No refcount types + let x = 1 + call assert_equal(-1, test_refcount(x)) + let x = 's' + call assert_equal(-1, test_refcount(x)) + let x = v:true + call assert_equal(-1, test_refcount(x)) + if has('float') + let x = 0.1 + call assert_equal(-1, test_refcount(x)) + endif + + " Check refcount + let x = [] + call assert_equal(1, test_refcount(x)) + + let x = {} + call assert_equal(1, test_refcount(x)) + + let x = 0zff + call assert_equal(1, test_refcount(x)) + + let X = {-> line('.')} + call assert_equal(1, test_refcount(X)) + let Y = X + call assert_equal(2, test_refcount(X)) + + if has('job') + let job = job_start([&shell, &shellcmdflag, 'echo .']) + call assert_equal(1, test_refcount(job)) + call assert_equal(1, test_refcount(job_getchannel(job))) + call assert_equal(1, test_refcount(job)) + endif + + " Function arguments, copying and unassigning + func ExprCheck(x, i) + let i = a:i + 1 + call assert_equal(i, test_refcount(a:x)) + let Y = a:x + call assert_equal(i + 1, test_refcount(a:x)) + call assert_equal(test_refcount(a:x), test_refcount(Y)) + let Y = 0 + call assert_equal(i, test_refcount(a:x)) + endfunc + call ExprCheck([], 0) + call ExprCheck({}, 0) + call ExprCheck(0zff, 0) + call ExprCheck({-> line('.')}, 0) + if has('job') + call ExprCheck(job, 1) + call ExprCheck(job_getchannel(job), 1) + call job_stop(job) + endif + delfunc ExprCheck + + " Regarding function + func Func(x) abort + call assert_equal(2, test_refcount(function('Func'))) + call assert_equal(0, test_refcount(funcref('Func'))) + endfunc + call assert_equal(1, test_refcount(function('Func'))) + call assert_equal(0, test_refcount(function('Func', [1]))) + call assert_equal(0, test_refcount(funcref('Func'))) + call assert_equal(0, test_refcount(funcref('Func', [1]))) + let X = function('Func') + let Y = X + call assert_equal(1, test_refcount(X)) + let X = function('Func', [1]) + let Y = X + call assert_equal(2, test_refcount(X)) + let X = funcref('Func') + let Y = X + call assert_equal(2, test_refcount(X)) + let X = funcref('Func', [1]) + let Y = X + call assert_equal(2, test_refcount(X)) + unlet X + unlet Y + call Func(1) + delfunc Func + + " Function with dict + func DictFunc() dict + call assert_equal(3, test_refcount(self)) + endfunc + let d = {'Func': function('DictFunc')} + call assert_equal(1, test_refcount(d)) + call assert_equal(0, test_refcount(d.Func)) + call d.Func() + unlet d + delfunc DictFunc +endfunc + "------------------------------------------------------------------------------- " Modelines {{{1 " vim: ts=8 sw=4 tw=80 fdm=marker diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -776,6 +776,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1044, +/**/ 1043, /**/ 1042,