eval: Refactor item_lock

If I am not mistaking, this commit should not change any functionality.
This commit is contained in:
ZyX
2017-02-20 22:24:46 +03:00
parent f2c9fd312c
commit e2fbbb292f
3 changed files with 74 additions and 69 deletions

View File

@@ -3112,68 +3112,71 @@ static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock)
static void item_lock(typval_T *tv, int deep, int lock) static void item_lock(typval_T *tv, int deep, int lock)
{ {
static int recurse = 0; static int recurse = 0;
list_T *l;
listitem_T *li;
dict_T *d;
hashitem_T *hi;
int todo;
if (recurse >= DICT_MAXNEST) { if (recurse >= DICT_MAXNEST) {
EMSG(_("E743: variable nested too deep for (un)lock")); EMSG(_("E743: variable nested too deep for (un)lock"));
return; return;
} }
if (deep == 0) if (deep == 0) {
return; return;
++recurse; }
recurse++;
/* lock/unlock the item itself */ // lock/unlock the item itself
if (lock) #define CHANGE_LOCK(var, lock) \
tv->v_lock |= VAR_LOCKED; do { \
else var = ((VarLockStatus[]) { \
tv->v_lock &= ~VAR_LOCKED; [VAR_UNLOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \
[VAR_LOCKED] = (lock ? VAR_LOCKED : VAR_UNLOCKED), \
[VAR_FIXED] = VAR_FIXED, \
})[var]; \
} while (0)
CHANGE_LOCK(tv->v_lock, lock);
switch (tv->v_type) { switch (tv->v_type) {
case VAR_LIST: case VAR_LIST: {
if ((l = tv->vval.v_list) != NULL) { list_T *const l = tv->vval.v_list;
if (lock) if (l != NULL) {
l->lv_lock |= VAR_LOCKED; CHANGE_LOCK(l->lv_lock, lock);
else
l->lv_lock &= ~VAR_LOCKED;
if (deep < 0 || deep > 1) if (deep < 0 || deep > 1)
/* recursive: lock/unlock the items the List contains */ // Recursive: lock/unlock the items the List contains.
for (li = l->lv_first; li != NULL; li = li->li_next) for (listitem_T *li = l->lv_first; li != NULL; li = li->li_next) {
item_lock(&li->li_tv, deep - 1, lock); item_lock(&li->li_tv, deep - 1, lock);
} }
}
break; break;
case VAR_DICT: }
if ((d = tv->vval.v_dict) != NULL) { case VAR_DICT: {
if (lock) dict_T *const d = tv->vval.v_dict;
d->dv_lock |= VAR_LOCKED; if (d != NULL) {
else CHANGE_LOCK(d->dv_lock, lock);
d->dv_lock &= ~VAR_LOCKED;
if (deep < 0 || deep > 1) { if (deep < 0 || deep > 1) {
/* recursive: lock/unlock the items the List contains */ // Recursive: lock/unlock the items the List contains.
todo = (int)d->dv_hashtab.ht_used; int todo = (int)d->dv_hashtab.ht_used;
for (hi = d->dv_hashtab.ht_array; todo > 0; ++hi) { for (hashitem_T *hi = d->dv_hashtab.ht_array; todo > 0; hi++) {
if (!HASHITEM_EMPTY(hi)) { if (!HASHITEM_EMPTY(hi)) {
--todo; todo--;
item_lock(&HI2DI(hi)->di_tv, deep - 1, lock); item_lock(&HI2DI(hi)->di_tv, deep - 1, lock);
} }
} }
} }
} }
break; break;
}
case VAR_NUMBER: case VAR_NUMBER:
case VAR_FLOAT: case VAR_FLOAT:
case VAR_STRING: case VAR_STRING:
case VAR_FUNC: case VAR_FUNC:
case VAR_PARTIAL: case VAR_PARTIAL:
case VAR_SPECIAL: case VAR_SPECIAL: {
break; break;
case VAR_UNKNOWN: }
case VAR_UNKNOWN: {
assert(false); assert(false);
} }
--recurse; }
#undef CHANGE_LOCK
recurse--;
} }
/* /*
@@ -6350,7 +6353,7 @@ dict_T *dict_alloc(void) FUNC_ATTR_NONNULL_RET
first_dict = d; first_dict = d;
hash_init(&d->dv_hashtab); hash_init(&d->dv_hashtab);
d->dv_lock = 0; d->dv_lock = VAR_UNLOCKED;
d->dv_scope = 0; d->dv_scope = 0;
d->dv_refcount = 0; d->dv_refcount = 0;
d->dv_copyID = 0; d->dv_copyID = 0;
@@ -20293,7 +20296,7 @@ void new_script_vars(scid_T id)
void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope) void init_var_dict(dict_T *dict, dictitem_T *dict_var, int scope)
{ {
hash_init(&dict->dv_hashtab); hash_init(&dict->dv_hashtab);
dict->dv_lock = 0; dict->dv_lock = VAR_UNLOCKED;
dict->dv_scope = scope; dict->dv_scope = scope;
dict->dv_refcount = DO_NOT_FREE_CNT; dict->dv_refcount = DO_NOT_FREE_CNT;
dict->dv_copyID = 0; dict->dv_copyID = 0;

View File

@@ -30,8 +30,8 @@ typedef enum {
/// Variable lock status for typval_T.v_lock /// Variable lock status for typval_T.v_lock
typedef enum { typedef enum {
VAR_UNLOCKED = 0, ///< Not locked. VAR_UNLOCKED = 0, ///< Not locked.
VAR_LOCKED, ///< User lock, can be unlocked. VAR_LOCKED = 1, ///< User lock, can be unlocked.
VAR_FIXED, ///< Locked forever. VAR_FIXED = 2, ///< Locked forever.
} VarLockStatus; } VarLockStatus;
/// VimL variable types, for use in typval_T.v_type /// VimL variable types, for use in typval_T.v_type
@@ -93,18 +93,18 @@ struct listwatch_S {
* Structure to hold info about a list. * Structure to hold info about a list.
*/ */
struct listvar_S { struct listvar_S {
listitem_T *lv_first; /* first item, NULL if none */ listitem_T *lv_first; ///< First item, NULL if none.
listitem_T *lv_last; /* last item, NULL if none */ listitem_T *lv_last; ///< Last item, NULL if none.
int lv_refcount; /* reference count */ int lv_refcount; ///< Reference count.
int lv_len; /* number of items */ int lv_len; ///< Number of items.
listwatch_T *lv_watch; /* first watcher, NULL if none */ listwatch_T *lv_watch; ///< First watcher, NULL if none.
int lv_idx; /* cached index of an item */ int lv_idx; ///< Index of a cached item, used for optimising repeated l[idx].
listitem_T *lv_idx_item; /* when not NULL item at index "lv_idx" */ listitem_T *lv_idx_item; ///< When not NULL item at index "lv_idx".
int lv_copyID; /* ID used by deepcopy() */ int lv_copyID; ///< ID used by deepcopy().
list_T *lv_copylist; /* copied list used by deepcopy() */ list_T *lv_copylist; ///< Copied list used by deepcopy().
char lv_lock; /* zero, VAR_LOCKED, VAR_FIXED */ VarLockStatus lv_lock; ///< Zero, VAR_LOCKED, VAR_FIXED.
list_T *lv_used_next; /* next list in used lists list */ list_T *lv_used_next; ///< next list in used lists list.
list_T *lv_used_prev; /* previous list in used lists list */ list_T *lv_used_prev; ///< Previous list in used lists list.
}; };
// Static list with 10 items. Use init_static_list() to initialize. // Static list with 10 items. Use init_static_list() to initialize.

View File

@@ -95,17 +95,19 @@ describe('b:changedtick', function()
redir_exec(':let b:')) redir_exec(':let b:'))
end) end)
it('fails to unlock b:changedtick', function() it('fails to unlock b:changedtick', function()
-- Note:
-- - unlocking VAR_FIXED variables is not an error.
-- - neither VAR_FIXED variables are reported as locked by islocked().
-- So test mostly checks that b:changedtick status does not change.
eq(0, exc_exec('let d = b:')) eq(0, exc_exec('let d = b:'))
eq(1, funcs.islocked('b:changedtick')) eq(1, funcs.islocked('b:changedtick'))
-- FIXME eq(0, funcs.islocked('d.changedtick'))
-- eq(1, funcs.islocked('d.changedtick'))
eq('\nE46: Cannot change read-only variable "b:changedtick"', eq('\nE46: Cannot change read-only variable "b:changedtick"',
redir_exec('unlockvar b:changedtick')) redir_exec('unlockvar b:changedtick'))
-- FIXME eq('',
-- eq('\nE46: Cannot change read-only variable "b:changedtick"', redir_exec('unlockvar d.changedtick'))
-- redir_exec('unlockvar d.changedtick'))
eq(1, funcs.islocked('b:changedtick')) eq(1, funcs.islocked('b:changedtick'))
-- eq(1, funcs.islocked('d.changedtick')) eq(0, funcs.islocked('d.changedtick'))
end) end)
it('is being completed', function() it('is being completed', function()
feed(':echo b:<Tab><Home>let cmdline="<End>"<CR>') feed(':echo b:<Tab><Home>let cmdline="<End>"<CR>')