patch 8.2.3435: Vim9: dict is not passed to dict function
Problem: Vim9: dict is not passed to dict function.
Solution: Keep the dict used until a function call.
diff --git a/src/vim9execute.c b/src/vim9execute.c
index 9fc942d..0fa1a12 100644
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -165,6 +165,75 @@
}
}
+static garray_T dict_stack = GA_EMPTY;
+
+/*
+ * Put a value on the dict stack. This consumes "tv".
+ */
+ static int
+dict_stack_save(typval_T *tv)
+{
+ if (dict_stack.ga_growsize == 0)
+ ga_init2(&dict_stack, (int)sizeof(typval_T), 10);
+ if (ga_grow(&dict_stack, 1) == FAIL)
+ return FAIL;
+ ((typval_T *)dict_stack.ga_data)[dict_stack.ga_len] = *tv;
+ ++dict_stack.ga_len;
+ return OK;
+}
+
+/*
+ * Get the typval at top of the dict stack.
+ */
+ static typval_T *
+dict_stack_get_tv(void)
+{
+ if (dict_stack.ga_len == 0)
+ return NULL;
+ return ((typval_T *)dict_stack.ga_data) + dict_stack.ga_len - 1;
+}
+
+/*
+ * Get the dict at top of the dict stack.
+ */
+ static dict_T *
+dict_stack_get_dict(void)
+{
+ typval_T *tv;
+
+ if (dict_stack.ga_len == 0)
+ return NULL;
+ tv = ((typval_T *)dict_stack.ga_data) + dict_stack.ga_len - 1;
+ if (tv->v_type == VAR_DICT)
+ return tv->vval.v_dict;
+ return NULL;
+}
+
+/*
+ * Drop an item from the dict stack.
+ */
+ static void
+dict_stack_drop(void)
+{
+ if (dict_stack.ga_len == 0)
+ {
+ iemsg("Dict stack underflow");
+ return;
+ }
+ --dict_stack.ga_len;
+ clear_tv(((typval_T *)dict_stack.ga_data) + dict_stack.ga_len);
+}
+
+/*
+ * Drop items from the dict stack until the length is equal to "len".
+ */
+ static void
+dict_stack_clear(int len)
+{
+ while (dict_stack.ga_len > len)
+ dict_stack_drop();
+}
+
/*
* Call compiled function "cdf_idx" from compiled code.
* This adds a stack frame and sets the instruction pointer to the start of the
@@ -765,7 +834,8 @@
partial_T *pt,
int argcount,
ectx_T *ectx,
- isn_T *iptr)
+ isn_T *iptr,
+ dict_T *selfdict)
{
typval_T argvars[MAX_FUNC_ARGS];
funcexe_T funcexe;
@@ -807,11 +877,12 @@
return FAIL;
CLEAR_FIELD(funcexe);
funcexe.evaluate = TRUE;
+ funcexe.selfdict = selfdict != NULL ? selfdict : dict_stack_get_dict();
// Call the user function. Result goes in last position on the stack.
// TODO: add selfdict if there is one
error = call_user_func_check(ufunc, argcount, argvars,
- STACK_TV_BOT(-1), &funcexe, NULL);
+ STACK_TV_BOT(-1), &funcexe, funcexe.selfdict);
// Clear the arguments.
for (idx = 0; idx < argcount; ++idx)
@@ -864,7 +935,8 @@
char_u *name,
int argcount,
ectx_T *ectx,
- isn_T *iptr)
+ isn_T *iptr,
+ dict_T *selfdict)
{
ufunc_T *ufunc;
@@ -916,7 +988,7 @@
}
}
- return call_ufunc(ufunc, NULL, argcount, ectx, iptr);
+ return call_ufunc(ufunc, NULL, argcount, ectx, iptr, selfdict);
}
return FAIL;
@@ -932,6 +1004,7 @@
char_u *name = NULL;
int called_emsg_before = called_emsg;
int res = FAIL;
+ dict_T *selfdict = NULL;
if (tv->v_type == VAR_PARTIAL)
{
@@ -953,9 +1026,10 @@
for (i = 0; i < pt->pt_argc; ++i)
copy_tv(&pt->pt_argv[i], STACK_TV_BOT(-argcount + i));
}
+ selfdict = pt->pt_dict;
if (pt->pt_func != NULL)
- return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL);
+ return call_ufunc(pt->pt_func, pt, argcount, ectx, NULL, selfdict);
name = pt->pt_name;
}
@@ -973,7 +1047,7 @@
if (error != FCERR_NONE)
res = FAIL;
else
- res = call_by_name(fname, argcount, ectx, NULL);
+ res = call_by_name(fname, argcount, ectx, NULL, selfdict);
vim_free(tofree);
}
@@ -1325,7 +1399,7 @@
int called_emsg_before = called_emsg;
int res;
- res = call_by_name(name, argcount, ectx, iptr);
+ res = call_by_name(name, argcount, ectx, iptr, NULL);
if (res == FAIL && called_emsg == called_emsg_before)
{
dictitem_T *v;
@@ -1570,6 +1644,7 @@
{
int ret = FAIL;
int save_trylevel_at_start = ectx->ec_trylevel_at_start;
+ int dict_stack_len_at_start = dict_stack.ga_len;
// Start execution at the first instruction.
ectx->ec_iidx = 0;
@@ -4022,7 +4097,6 @@
dict_T *dict;
char_u *key;
dictitem_T *di;
- typval_T temp_tv;
// dict member: dict is at stack-2, key at stack-1
tv = STACK_TV_BOT(-2);
@@ -4041,23 +4115,24 @@
semsg(_(e_dictkey), key);
// If :silent! is used we will continue, make sure the
- // stack contents makes sense.
+ // stack contents makes sense and the dict stack is
+ // updated.
clear_tv(tv);
--ectx->ec_stack.ga_len;
tv = STACK_TV_BOT(-1);
- clear_tv(tv);
+ (void) dict_stack_save(tv);
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
goto on_fatal_error;
}
clear_tv(tv);
--ectx->ec_stack.ga_len;
- // Clear the dict only after getting the item, to avoid
- // that it makes the item invalid.
+ // Put the dict used on the dict stack, it might be used by
+ // a dict function later.
tv = STACK_TV_BOT(-1);
- temp_tv = *tv;
+ if (dict_stack_save(tv) == FAIL)
+ goto on_fatal_error;
copy_tv(&di->di_tv, tv);
- clear_tv(&temp_tv);
}
break;
@@ -4066,7 +4141,6 @@
{
dict_T *dict;
dictitem_T *di;
- typval_T temp_tv;
tv = STACK_TV_BOT(-1);
if (tv->v_type != VAR_DICT || tv->vval.v_dict == NULL)
@@ -4084,11 +4158,37 @@
semsg(_(e_dictkey), iptr->isn_arg.string);
goto on_error;
}
- // Clear the dict after getting the item, to avoid that it
- // make the item invalid.
- temp_tv = *tv;
+ // Put the dict used on the dict stack, it might be used by
+ // a dict function later.
+ if (dict_stack_save(tv) == FAIL)
+ goto on_fatal_error;
+
copy_tv(&di->di_tv, tv);
- clear_tv(&temp_tv);
+ }
+ break;
+
+ case ISN_CLEARDICT:
+ dict_stack_drop();
+ break;
+
+ case ISN_USEDICT:
+ {
+ typval_T *dict_tv = dict_stack_get_tv();
+
+ // Turn "dict.Func" into a partial for "Func" bound to
+ // "dict". Don't do this when "Func" is already a partial
+ // that was bound explicitly (pt_auto is FALSE).
+ tv = STACK_TV_BOT(-1);
+ if (dict_tv != NULL
+ && dict_tv->v_type == VAR_DICT
+ && dict_tv->vval.v_dict != NULL
+ && (tv->v_type == VAR_FUNC
+ || (tv->v_type == VAR_PARTIAL
+ && (tv->vval.v_partial->pt_auto
+ || tv->vval.v_partial->pt_dict == NULL))))
+ dict_tv->vval.v_dict =
+ make_partial(dict_tv->vval.v_dict, tv);
+ dict_stack_drop();
}
break;
@@ -4478,6 +4578,7 @@
done:
ret = OK;
theend:
+ dict_stack_clear(dict_stack_len_at_start);
ectx->ec_trylevel_at_start = save_trylevel_at_start;
return ret;
}
@@ -5568,6 +5669,9 @@
case ISN_MEMBER: smsg("%s%4d MEMBER", pfx, current); break;
case ISN_STRINGMEMBER: smsg("%s%4d MEMBER %s", pfx, current,
iptr->isn_arg.string); break;
+ case ISN_CLEARDICT: smsg("%s%4d CLEARDICT", pfx, current); break;
+ case ISN_USEDICT: smsg("%s%4d USEDICT", pfx, current); break;
+
case ISN_NEGATENR: smsg("%s%4d NEGATENR", pfx, current); break;
case ISN_CHECKNR: smsg("%s%4d CHECKNR", pfx, current); break;