Problem: Vim9: cannot lock a member in a local dict.
Solution: Get the local dict from the stack and pass it to get_lval().
if ((*p != '[' && *p != '.') || lp->ll_name == NULL)
return p;
- 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,
+ if (in_vim9script() && lval_root != NULL)
+ {
+ // using local variable
+ lp->ll_tv = lval_root;
+ }
+ 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);
- if (v == NULL && !quiet)
- semsg(_(e_undefined_variable_str), lp->ll_name);
- *p = cc;
- if (v == NULL)
- return NULL;
+ if (v == NULL && !quiet)
+ semsg(_(e_undefined_variable_str), lp->ll_name);
+ *p = cc;
+ if (v == NULL)
+ return NULL;
+ lp->ll_tv = &v->di_tv;
+ }
if (in_vim9script() && (flags & GLV_NO_DECL) == 0)
{
/*
* Loop until no more [idx] or .key is following.
*/
- lp->ll_tv = &v->di_tv;
var1.v_type = VAR_UNKNOWN;
var2.v_type = VAR_UNKNOWN;
while (*p == '[' || (*p == '.' && p[1] != '=' && p[1] != '.'))
#endif
#ifdef FEAT_EVAL
EXTERN int input_busy INIT(= 0); // when inside get_user_input() then > 0
+
+EXTERN typval_T *lval_root INIT(= NULL);
#endif
#ifdef FEAT_BEVAL_TERM
s:theList[1] = 44
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
vim9script
var theList = [1, 2, 3]
res)
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()
try
echo "yes"
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 3339,
/**/
3338,
/**/
ISN_UNLETINDEX, // unlet item of list or dict
ISN_UNLETRANGE, // unlet items of list
+ ISN_LOCKUNLOCK, // :lock and :unlock for local variable member
ISN_LOCKCONST, // lock constant value
// constants
}
static int
-generate_EXEC(cctx_T *cctx, char_u *line)
+generate_EXEC(cctx_T *cctx, isntype_T isntype, char_u *line)
{
isn_T *isn;
RETURN_OK_IF_SKIP(cctx);
- if ((isn = generate_instr(cctx, ISN_EXEC)) == NULL)
+ if ((isn = generate_instr(cctx, isntype)) == NULL)
return FAIL;
isn->isn_arg.string = vim_strsave(line);
return OK;
int ret = OK;
size_t len;
char_u *buf;
+ isntype_T isn = ISN_EXEC;
if (cctx->ctx_skip == SKIP_YES)
return OK;
if (lookup_local(p, end - p, NULL, cctx) == OK)
{
- emsg(_(e_cannot_lock_unlock_local_variable));
- return FAIL;
+ char_u *s = p;
+
+ 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;
}
}
vim_snprintf((char *)buf, len, "%s %s",
eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
p);
- ret = generate_EXEC(cctx, buf);
+ ret = generate_EXEC(cctx, isn, buf);
vim_free(buf);
*name_end = cc;
generate_EXECCONCAT(cctx, count);
}
else
- generate_EXEC(cctx, line);
+ generate_EXEC(cctx, ISN_EXEC, line);
theend:
if (*nextcmd != NUL)
case ISN_LOADOPT:
case ISN_LOADT:
case ISN_LOADW:
+ case ISN_LOCKUNLOCK:
case ISN_PUSHEXC:
case ISN_PUSHFUNC:
case ISN_PUSHS:
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
struct instr_S {
ectx_T *instr_ectx;
{
// execute Ex command line
case ISN_EXEC:
- {
- 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)
- goto on_error;
- }
+ if (exec_command(iptr) == FAIL)
+ goto on_error;
break;
// execute Ex command line split at NL characters.
vim_unsetenv(iptr->isn_arg.unlet.ul_name);
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:
item_lock(STACK_TV_BOT(-1), 100, TRUE, TRUE);
break;
case ISN_UNLETRANGE:
smsg("%s%4d UNLETRANGE", pfx, current);
break;
+ case ISN_LOCKUNLOCK:
+ smsg("%s%4d LOCKUNLOCK %s", pfx, current, iptr->isn_arg.string);
+ break;
case ISN_LOCKCONST:
smsg("%s%4d LOCKCONST", pfx, current);
break;