forked from aniani/vim
patch 8.2.3339: Vim9: cannot lock a member in a local dict
Problem: Vim9: cannot lock a member in a local dict. Solution: Get the local dict from the stack and pass it to get_lval().
This commit is contained in:
parent
89071cb6a1
commit
aacc966c5d
30
src/eval.c
30
src/eval.c
@ -902,17 +902,26 @@ get_lval(
|
|||||||
if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
|
if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
|
||||||
return p;
|
return p;
|
||||||
|
|
||||||
cc = *p;
|
if (in_vim9script() && lval_root != NULL)
|
||||||
*p = NUL;
|
{
|
||||||
// When we would write to the variable pass &ht and prevent autoload.
|
// using local variable
|
||||||
writing = !(flags & GLV_READ_ONLY);
|
lp->ll_tv = lval_root;
|
||||||
v = find_var(lp->ll_name, writing ? &ht : NULL,
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cc = *p;
|
||||||
|
*p = NUL;
|
||||||
|
// When we would write to the variable pass &ht and prevent autoload.
|
||||||
|
writing = !(flags & GLV_READ_ONLY);
|
||||||
|
v = find_var(lp->ll_name, writing ? &ht : NULL,
|
||||||
(flags & GLV_NO_AUTOLOAD) || writing);
|
(flags & GLV_NO_AUTOLOAD) || writing);
|
||||||
if (v == NULL && !quiet)
|
if (v == NULL && !quiet)
|
||||||
semsg(_(e_undefined_variable_str), lp->ll_name);
|
semsg(_(e_undefined_variable_str), lp->ll_name);
|
||||||
*p = cc;
|
*p = cc;
|
||||||
if (v == NULL)
|
if (v == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
lp->ll_tv = &v->di_tv;
|
||||||
|
}
|
||||||
|
|
||||||
if (in_vim9script() && (flags & GLV_NO_DECL) == 0)
|
if (in_vim9script() && (flags & GLV_NO_DECL) == 0)
|
||||||
{
|
{
|
||||||
@ -924,7 +933,6 @@ get_lval(
|
|||||||
/*
|
/*
|
||||||
* Loop until no more [idx] or .key is following.
|
* Loop until no more [idx] or .key is following.
|
||||||
*/
|
*/
|
||||||
lp->ll_tv = &v->di_tv;
|
|
||||||
var1.v_type = VAR_UNKNOWN;
|
var1.v_type = VAR_UNKNOWN;
|
||||||
var2.v_type = VAR_UNKNOWN;
|
var2.v_type = VAR_UNKNOWN;
|
||||||
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
|
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
|
||||||
|
@ -1835,6 +1835,8 @@ EXTERN int timer_busy INIT(= 0); // when timer is inside vgetc() then > 0
|
|||||||
#endif
|
#endif
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
EXTERN int input_busy INIT(= 0); // when inside get_user_input() then > 0
|
EXTERN int input_busy INIT(= 0); // when inside get_user_input() then > 0
|
||||||
|
|
||||||
|
EXTERN typval_T *lval_root INIT(= NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef FEAT_BEVAL_TERM
|
#ifdef FEAT_BEVAL_TERM
|
||||||
|
@ -1195,6 +1195,23 @@ def Test_lockvar()
|
|||||||
s:theList[1] = 44
|
s:theList[1] = 44
|
||||||
assert_equal([1, 44, 3], s:theList)
|
assert_equal([1, 44, 3], s:theList)
|
||||||
|
|
||||||
|
var d = {a: 1, b: 2}
|
||||||
|
d.a = 3
|
||||||
|
d.b = 4
|
||||||
|
assert_equal({a: 3, b: 4}, d)
|
||||||
|
lockvar d.a
|
||||||
|
d.b = 5
|
||||||
|
var ex = ''
|
||||||
|
try
|
||||||
|
d.a = 6
|
||||||
|
catch
|
||||||
|
ex = v:exception
|
||||||
|
endtry
|
||||||
|
assert_match('E1121:', ex)
|
||||||
|
unlockvar d.a
|
||||||
|
d.a = 7
|
||||||
|
assert_equal({a: 7, b: 5}, d)
|
||||||
|
|
||||||
var lines =<< trim END
|
var lines =<< trim END
|
||||||
vim9script
|
vim9script
|
||||||
var theList = [1, 2, 3]
|
var theList = [1, 2, 3]
|
||||||
|
@ -588,6 +588,25 @@ def Test_disassemble_unlet()
|
|||||||
res)
|
res)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
def s:LockLocal()
|
||||||
|
var d = {a: 1}
|
||||||
|
lockvar d.a
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_locl_local()
|
||||||
|
var res = execute('disass s:LockLocal')
|
||||||
|
assert_match('<SNR>\d*_LockLocal\_s*' ..
|
||||||
|
'var d = {a: 1}\_s*' ..
|
||||||
|
'\d PUSHS "a"\_s*' ..
|
||||||
|
'\d PUSHNR 1\_s*' ..
|
||||||
|
'\d NEWDICT size 1\_s*' ..
|
||||||
|
'\d STORE $0\_s*' ..
|
||||||
|
'lockvar d.a\_s*' ..
|
||||||
|
'\d LOAD $0\_s*' ..
|
||||||
|
'\d LOCKUNLOCK lockvar d.a\_s*',
|
||||||
|
res)
|
||||||
|
enddef
|
||||||
|
|
||||||
def s:ScriptFuncTry()
|
def s:ScriptFuncTry()
|
||||||
try
|
try
|
||||||
echo "yes"
|
echo "yes"
|
||||||
|
@ -755,6 +755,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 */
|
||||||
|
/**/
|
||||||
|
3339,
|
||||||
/**/
|
/**/
|
||||||
3338,
|
3338,
|
||||||
/**/
|
/**/
|
||||||
|
@ -70,6 +70,7 @@ typedef enum {
|
|||||||
ISN_UNLETINDEX, // unlet item of list or dict
|
ISN_UNLETINDEX, // unlet item of list or dict
|
||||||
ISN_UNLETRANGE, // unlet items of list
|
ISN_UNLETRANGE, // unlet items of list
|
||||||
|
|
||||||
|
ISN_LOCKUNLOCK, // :lock and :unlock for local variable member
|
||||||
ISN_LOCKCONST, // lock constant value
|
ISN_LOCKCONST, // lock constant value
|
||||||
|
|
||||||
// constants
|
// constants
|
||||||
|
@ -2262,12 +2262,12 @@ generate_PUT(cctx_T *cctx, int regname, linenr_T lnum)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
generate_EXEC(cctx_T *cctx, char_u *line)
|
generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line)
|
||||||
{
|
{
|
||||||
isn_T *isn;
|
isn_T *isn;
|
||||||
|
|
||||||
RETURN_OK_IF_SKIP(cctx);
|
RETURN_OK_IF_SKIP(cctx);
|
||||||
if ((isn = generate_instr(cctx, ISN_EXEC)) == NULL)
|
if ((isn = generate_instr(cctx, isntype)) == NULL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
isn->isn_arg.string = vim_strsave(line);
|
isn->isn_arg.string = vim_strsave(line);
|
||||||
return OK;
|
return OK;
|
||||||
@ -7426,6 +7426,7 @@ compile_lock_unlock(
|
|||||||
int ret = OK;
|
int ret = OK;
|
||||||
size_t len;
|
size_t len;
|
||||||
char_u *buf;
|
char_u *buf;
|
||||||
|
isntype_T isn = ISN_EXEC;
|
||||||
|
|
||||||
if (cctx->ctx_skip == SKIP_YES)
|
if (cctx->ctx_skip == SKIP_YES)
|
||||||
return OK;
|
return OK;
|
||||||
@ -7437,8 +7438,19 @@ compile_lock_unlock(
|
|||||||
|
|
||||||
if (lookup_local(p, end - p, NULL, cctx) == OK)
|
if (lookup_local(p, end - p, NULL, cctx) == OK)
|
||||||
{
|
{
|
||||||
emsg(_(e_cannot_lock_unlock_local_variable));
|
char_u *s = p;
|
||||||
return FAIL;
|
|
||||||
|
if (*end != '.' && *end != '[')
|
||||||
|
{
|
||||||
|
emsg(_(e_cannot_lock_unlock_local_variable));
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// For "d.member" put the local variable on the stack, it will be
|
||||||
|
// passed to ex_lockvar() indirectly.
|
||||||
|
if (compile_load(&s, end, cctx, FALSE, FALSE) == FAIL)
|
||||||
|
return FAIL;
|
||||||
|
isn = ISN_LOCKUNLOCK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7453,7 +7465,7 @@ compile_lock_unlock(
|
|||||||
vim_snprintf((char *)buf, len, "%s %s",
|
vim_snprintf((char *)buf, len, "%s %s",
|
||||||
eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
|
eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
|
||||||
p);
|
p);
|
||||||
ret = generate_EXEC(cctx, buf);
|
ret = generate_EXEC(cctx, isn, buf);
|
||||||
|
|
||||||
vim_free(buf);
|
vim_free(buf);
|
||||||
*name_end = cc;
|
*name_end = cc;
|
||||||
@ -9110,7 +9122,7 @@ compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx)
|
|||||||
generate_EXECCONCAT(cctx, count);
|
generate_EXECCONCAT(cctx, count);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
generate_EXEC(cctx, line);
|
generate_EXEC(cctx, ISN_EXEC, line);
|
||||||
|
|
||||||
theend:
|
theend:
|
||||||
if (*nextcmd != NUL)
|
if (*nextcmd != NUL)
|
||||||
@ -10198,6 +10210,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_LOADOPT:
|
case ISN_LOADOPT:
|
||||||
case ISN_LOADT:
|
case ISN_LOADT:
|
||||||
case ISN_LOADW:
|
case ISN_LOADW:
|
||||||
|
case ISN_LOCKUNLOCK:
|
||||||
case ISN_PUSHEXC:
|
case ISN_PUSHEXC:
|
||||||
case ISN_PUSHFUNC:
|
case ISN_PUSHFUNC:
|
||||||
case ISN_PUSHS:
|
case ISN_PUSHS:
|
||||||
|
@ -1396,6 +1396,27 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute iptr->isn_arg.string as an Ex command.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
exec_command(isn_T *iptr)
|
||||||
|
{
|
||||||
|
source_cookie_T cookie;
|
||||||
|
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
// Pass getsourceline to get an error for a missing ":end"
|
||||||
|
// command.
|
||||||
|
CLEAR_FIELD(cookie);
|
||||||
|
cookie.sourcing_lnum = iptr->isn_lnum - 1;
|
||||||
|
if (do_cmdline(iptr->isn_arg.string,
|
||||||
|
getsourceline, &cookie,
|
||||||
|
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED) == FAIL
|
||||||
|
|| did_emsg)
|
||||||
|
return FAIL;
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
// used for v_instr of typval of VAR_INSTR
|
// used for v_instr of typval of VAR_INSTR
|
||||||
struct instr_S {
|
struct instr_S {
|
||||||
ectx_T *instr_ectx;
|
ectx_T *instr_ectx;
|
||||||
@ -1637,21 +1658,8 @@ exec_instructions(ectx_T *ectx)
|
|||||||
{
|
{
|
||||||
// execute Ex command line
|
// execute Ex command line
|
||||||
case ISN_EXEC:
|
case ISN_EXEC:
|
||||||
{
|
if (exec_command(iptr) == FAIL)
|
||||||
source_cookie_T cookie;
|
goto on_error;
|
||||||
|
|
||||||
SOURCING_LNUM = iptr->isn_lnum;
|
|
||||||
// Pass getsourceline to get an error for a missing ":end"
|
|
||||||
// command.
|
|
||||||
CLEAR_FIELD(cookie);
|
|
||||||
cookie.sourcing_lnum = iptr->isn_lnum - 1;
|
|
||||||
if (do_cmdline(iptr->isn_arg.string,
|
|
||||||
getsourceline, &cookie,
|
|
||||||
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED)
|
|
||||||
== FAIL
|
|
||||||
|| did_emsg)
|
|
||||||
goto on_error;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// execute Ex command line split at NL characters.
|
// execute Ex command line split at NL characters.
|
||||||
@ -2880,6 +2888,23 @@ exec_instructions(ectx_T *ectx)
|
|||||||
vim_unsetenv(iptr->isn_arg.unlet.ul_name);
|
vim_unsetenv(iptr->isn_arg.unlet.ul_name);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case ISN_LOCKUNLOCK:
|
||||||
|
{
|
||||||
|
typval_T *lval_root_save = lval_root;
|
||||||
|
int res;
|
||||||
|
|
||||||
|
// Stack has the local variable, argument the whole :lock
|
||||||
|
// or :unlock command, like ISN_EXEC.
|
||||||
|
--ectx->ec_stack.ga_len;
|
||||||
|
lval_root = STACK_TV_BOT(0);
|
||||||
|
res = exec_command(iptr);
|
||||||
|
clear_tv(lval_root);
|
||||||
|
lval_root = lval_root_save;
|
||||||
|
if (res == FAIL)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case ISN_LOCKCONST:
|
case ISN_LOCKCONST:
|
||||||
item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE);
|
item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE);
|
||||||
break;
|
break;
|
||||||
@ -5244,6 +5269,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case ISN_UNLETRANGE:
|
case ISN_UNLETRANGE:
|
||||||
smsg("%s%4d UNLETRANGE", pfx, current);
|
smsg("%s%4d UNLETRANGE", pfx, current);
|
||||||
break;
|
break;
|
||||||
|
case ISN_LOCKUNLOCK:
|
||||||
|
smsg("%s%4d LOCKUNLOCK %s", pfx, current, iptr->isn_arg.string);
|
||||||
|
break;
|
||||||
case ISN_LOCKCONST:
|
case ISN_LOCKCONST:
|
||||||
smsg("%s%4d LOCKCONST", pfx, current);
|
smsg("%s%4d LOCKCONST", pfx, current);
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user