Patch 8.2.0679
Problem: Vim9: incomplete support for closures.
Solution: At the end of a function copy arguments and local variables if
they are still used by a referenced closure.
Files: src/structs.h, src/vim9.h, src/vim9compile.c, src/vim9execute.c,
src/testdir/test_vim9_func.vim
*** ../vim-8.2.0678/src/structs.h 2020-05-01 19:29:05.006157706 +0200
--- src/structs.h 2020-05-01 21:34:16.224777578 +0200
***************
*** 1563,1570 ****
int uf_refcount; // reference count, see
func_name_refcount()
funccall_T *uf_scoped; // l: local variables for closure
- garray_T *uf_ectx_stack; // where compiled closure finds local vars
- int uf_ectx_frame; // index of function frame in
uf_ectx_stack
char_u *uf_name_exp; // if "uf_name[]" starts with SNR the name with
// "<SNR>" as a string, otherwise NULL
--- 1563,1568 ----
***************
*** 1591,1597 ****
#define FIXVAR_CNT 12 // number of fixed variables
/*
! * structure to hold info for a function that is currently being executed.
*/
struct funccall_S
{
--- 1589,1595 ----
#define FIXVAR_CNT 12 // number of fixed variables
/*
! * Structure to hold info for a function that is currently being executed.
*/
struct funccall_S
{
*** ../vim-8.2.0678/src/vim9.h 2020-05-01 19:29:05.006157706 +0200
--- src/vim9.h 2020-05-02 13:21:48.509775530 +0200
***************
*** 71,77 ****
ISN_PCALL, // call partial, use isn_arg.pfunc
ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set
ISN_RETURN, // return, result is on top of stack
! ISN_FUNCREF, // push a function ref to dfunc isn_arg.number
// expression operations
ISN_JUMP, // jump if condition is matched isn_arg.jump
--- 71,77 ----
ISN_PCALL, // call partial, use isn_arg.pfunc
ISN_PCALL_END, // cleanup after ISN_PCALL with cpf_top set
ISN_RETURN, // return, result is on top of stack
! ISN_FUNCREF, // push a function ref to dfunc isn_arg.funcref
// expression operations
ISN_JUMP, // jump if condition is matched isn_arg.jump
***************
*** 218,223 ****
--- 218,229 ----
int ul_forceit; // forceit flag
} unlet_T;
+ // arguments to ISN_FUNCREF
+ typedef struct {
+ int fr_func; // function index
+ int fr_var_idx; // variable to store partial
+ } funcref_T;
+
/*
* Instruction
*/
***************
*** 249,258 ****
--- 255,280 ----
loadstore_T loadstore;
script_T script;
unlet_T unlet;
+ funcref_T funcref;
} isn_arg;
};
/*
+ * Structure to hold the context of a compiled function, used by closures
+ * defined in that function.
+ */
+ typedef struct funcstack_S
+ {
+ garray_T fs_ga; // contains the stack, with:
+ // - arguments
+ // - frame
+ // - local variables
+
+ int fs_refcount; // nr of closures referencing this
funcstack
+ int fs_copyID; // for garray_T collection
+ } funcstack_T;
+
+ /*
* Info about a function defined with :def. Used in "def_functions".
*/
struct dfunc_S {
***************
*** 264,273 ****
--- 286,304 ----
isn_T *df_instr; // function body to be executed
int df_instr_count;
+ garray_T *df_ectx_stack; // where compiled closure finds local vars
+ int df_ectx_frame; // index of function frame in
uf_ectx_stack
+ funcstack_T *df_funcstack; // copy of stack for closure, used
after
+ // closure context function returns
+
int df_varcount; // number of local variables
+ int df_closure_count; // number of closures created
};
// Number of entries used by stack frame for a function call.
+ // - function index
+ // - instruction index
+ // - previous frame index
#define STACK_FRAME_SIZE 3
*** ../vim-8.2.0678/src/vim9compile.c 2020-05-01 19:29:05.002157723 +0200
--- src/vim9compile.c 2020-05-02 17:39:31.230347161 +0200
***************
*** 116,121 ****
--- 116,124 ----
garray_T ctx_locals; // currently visible local variables
int ctx_locals_count; // total number of local variables
+ int ctx_closure_count; // number of closures created in the
+ // function
+
garray_T ctx_imports; // imported items
int ctx_skip; // when TRUE skip commands, when
FALSE skip
***************
*** 1254,1260 ****
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL;
! isn->isn_arg.number = dfunc_idx;
if (ga_grow(stack, 1) == FAIL)
return FAIL;
--- 1257,1264 ----
RETURN_OK_IF_SKIP(cctx);
if ((isn = generate_instr(cctx, ISN_FUNCREF)) == NULL)
return FAIL;
! isn->isn_arg.funcref.fr_func = dfunc_idx;
! isn->isn_arg.funcref.fr_var_idx = cctx->ctx_closure_count++;
if (ga_grow(stack, 1) == FAIL)
return FAIL;
***************
*** 6395,6400 ****
--- 6399,6405 ----
dfunc->df_instr = instr->ga_data;
dfunc->df_instr_count = instr->ga_len;
dfunc->df_varcount = cctx.ctx_locals_count;
+ dfunc->df_closure_count = cctx.ctx_closure_count;
if (cctx.ctx_outer_used)
ufunc->uf_flags |= FC_CLOSURE;
}
***************
*** 6620,6625 ****
--- 6625,6647 ----
delete_instr(dfunc->df_instr + idx);
VIM_CLEAR(dfunc->df_instr);
}
+ if (dfunc->df_funcstack != NULL)
+ {
+ // Decrease the reference count for the context of a closure. If down
+ // to zero free it and clear the variables on the stack.
+ if (--dfunc->df_funcstack->fs_refcount == 0)
+ {
+ garray_T *gap = &dfunc->df_funcstack->fs_ga;
+ typval_T *stack = gap->ga_data;
+ int i;
+
+ for (i = 0; i < gap->ga_len; ++i)
+ clear_tv(stack + i);
+ ga_clear(gap);
+ vim_free(dfunc->df_funcstack);
+ }
+ dfunc->df_funcstack = NULL;
+ }
dfunc->df_deleted = TRUE;
}
*** ../vim-8.2.0678/src/vim9execute.c 2020-05-01 19:29:05.006157706 +0200
--- src/vim9execute.c 2020-05-02 15:53:28.736417890 +0200
***************
*** 24,30 ****
// Structure put on ec_trystack when ISN_TRY is encountered.
typedef struct {
! int tcd_frame; // ec_frame when ISN_TRY was encountered
int tcd_catch_idx; // instruction of the first catch
int tcd_finally_idx; // instruction of the finally block
int tcd_caught; // catch block entered
--- 24,30 ----
// Structure put on ec_trystack when ISN_TRY is encountered.
typedef struct {
! int tcd_frame_idx; // ec_frame_idx when ISN_TRY was
encountered
int tcd_catch_idx; // instruction of the first catch
int tcd_finally_idx; // instruction of the finally block
int tcd_caught; // catch block entered
***************
*** 56,62 ****
*/
typedef struct {
garray_T ec_stack; // stack of typval_T values
! int ec_frame; // index in ec_stack: context of
ec_dfunc_idx
garray_T *ec_outer_stack; // stack used for closures
int ec_outer_frame; // stack frame in ec_outer_stack
--- 56,62 ----
*/
typedef struct {
garray_T ec_stack; // stack of typval_T values
! int ec_frame_idx; // index in ec_stack: context of
ec_dfunc_idx
garray_T *ec_outer_stack; // stack used for closures
int ec_outer_frame; // stack frame in ec_outer_stack
***************
*** 202,208 ****
iemsg("Argument count wrong?");
return FAIL;
}
! if (ga_grow(&ectx->ec_stack, arg_to_add + 3 + dfunc->df_varcount) == FAIL)
return FAIL;
// Move the vararg-list to below the missing optional arguments.
--- 202,209 ----
iemsg("Argument count wrong?");
return FAIL;
}
! if (ga_grow(&ectx->ec_stack, arg_to_add + 3
! + dfunc->df_varcount + dfunc->df_closure_count) == FAIL)
return FAIL;
// Move the vararg-list to below the missing optional arguments.
***************
*** 215,231 ****
ectx->ec_stack.ga_len += arg_to_add;
// Store current execution state in stack frame for ISN_RETURN.
- // TODO: If the actual number of arguments doesn't match what the called
- // function expects things go bad.
STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
! STACK_TV_BOT(2)->vval.v_number = ectx->ec_frame;
! ectx->ec_frame = ectx->ec_stack.ga_len;
// Initialize local variables
! for (idx = 0; idx < dfunc->df_varcount; ++idx)
STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
! ectx->ec_stack.ga_len += STACK_FRAME_SIZE + dfunc->df_varcount;
// Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx;
--- 216,231 ----
ectx->ec_stack.ga_len += arg_to_add;
// Store current execution state in stack frame for ISN_RETURN.
STACK_TV_BOT(0)->vval.v_number = ectx->ec_dfunc_idx;
STACK_TV_BOT(1)->vval.v_number = ectx->ec_iidx;
! STACK_TV_BOT(2)->vval.v_number = ectx->ec_frame_idx;
! ectx->ec_frame_idx = ectx->ec_stack.ga_len;
// Initialize local variables
! for (idx = 0; idx < dfunc->df_varcount + dfunc->df_closure_count; ++idx)
STACK_TV_BOT(STACK_FRAME_SIZE + idx)->v_type = VAR_UNKNOWN;
! ectx->ec_stack.ga_len += STACK_FRAME_SIZE
! + dfunc->df_varcount + dfunc->df_closure_count;
// Set execution state to the start of the called function.
ectx->ec_dfunc_idx = cdf_idx;
***************
*** 233,240 ****
estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
// used for closures
! ectx->ec_outer_stack = ufunc->uf_ectx_stack;
! ectx->ec_outer_frame = ufunc->uf_ectx_frame;
// Decide where to start execution, handles optional arguments.
init_instr_idx(ufunc, argcount, ectx);
--- 233,240 ----
estack_push_ufunc(ETYPE_UFUNC, dfunc->df_ufunc, 1);
// used for closures
! ectx->ec_outer_stack = dfunc->df_ectx_stack;
! ectx->ec_outer_frame = dfunc->df_ectx_frame;
// Decide where to start execution, handles optional arguments.
init_instr_idx(ufunc, argcount, ectx);
***************
*** 246,279 ****
#define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
/*
* Return from the current function.
*/
! static void
func_return(ectx_T *ectx)
{
int idx;
! dfunc_T *dfunc;
! int top;
// execution context goes one level up
estack_pop();
! // Clear the local variables and temporary values, but not
! // the return value.
! for (idx = ectx->ec_frame + STACK_FRAME_SIZE;
! idx < ectx->ec_stack.ga_len - 1; ++idx)
! clear_tv(STACK_TV(idx));
// Clear the arguments.
! dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
! top = ectx->ec_frame - ufunc_argcount(dfunc->df_ufunc);
! for (idx = top; idx < ectx->ec_frame; ++idx)
clear_tv(STACK_TV(idx));
// Restore the previous frame.
! ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame)->vval.v_number;
! ectx->ec_iidx = STACK_TV(ectx->ec_frame + 1)->vval.v_number;
! ectx->ec_frame = STACK_TV(ectx->ec_frame + 2)->vval.v_number;
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
ectx->ec_instr = dfunc->df_instr;
--- 246,367 ----
#define STACK_TV(idx) (((typval_T *)ectx->ec_stack.ga_data) + idx)
/*
+ * Used when returning from a function: Check if any closure is still
+ * referenced. If so then move the arguments and variables to a separate
piece
+ * of stack to be used when the closure is called.
+ * When "free_arguments" is TRUE the arguments are to be freed.
+ * Returns FAIL when out of memory.
+ */
+ static int
+ handle_closure_in_use(ectx_T *ectx, int free_arguments)
+ {
+ dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ + ectx->ec_dfunc_idx;
+ int argcount = ufunc_argcount(dfunc->df_ufunc);
+ int top = ectx->ec_frame_idx - argcount;
+ int idx;
+ typval_T *tv;
+ int closure_in_use = FALSE;
+
+ // Check if any created closure is still in use.
+ for (idx = 0; idx < dfunc->df_closure_count; ++idx)
+ {
+ tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE
+ + dfunc->df_varcount + idx);
+ if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial->pt_refcount > 1)
+ closure_in_use = TRUE;
+ }
+
+ if (closure_in_use)
+ {
+ funcstack_T *funcstack = ALLOC_CLEAR_ONE(funcstack_T);
+ typval_T *stack;
+
+ // A closure is using the arguments and/or local variables.
+ // Move them to the called function.
+ if (funcstack == NULL)
+ return FAIL;
+ funcstack->fs_ga.ga_len = argcount + STACK_FRAME_SIZE
+ + dfunc->df_varcount;
+ stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len);
+ funcstack->fs_ga.ga_data = stack;
+ if (stack == NULL)
+ {
+ vim_free(funcstack);
+ return FAIL;
+ }
+
+ // Move or copy the arguments.
+ for (idx = 0; idx < argcount; ++idx)
+ {
+ tv = STACK_TV(top + idx);
+ if (free_arguments)
+ {
+ *(stack + idx) = *tv;
+ tv->v_type = VAR_UNKNOWN;
+ }
+ else
+ copy_tv(tv, stack + idx);
+ }
+ // Move the local variables.
+ for (idx = 0; idx < dfunc->df_varcount; ++idx)
+ {
+ tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE + idx);
+ *(stack + argcount + STACK_FRAME_SIZE + idx) = *tv;
+ tv->v_type = VAR_UNKNOWN;
+ }
+
+ for (idx = 0; idx < dfunc->df_closure_count; ++idx)
+ {
+ tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE
+ + dfunc->df_varcount + idx);
+ if (tv->v_type == VAR_PARTIAL
+ && tv->vval.v_partial->pt_refcount > 1)
+ {
+ dfunc_T *pt_dfunc = ((dfunc_T *)def_functions.ga_data)
+ + tv->vval.v_partial->pt_func->uf_dfunc_idx;
+ ++funcstack->fs_refcount;
+ pt_dfunc->df_funcstack = funcstack;
+ pt_dfunc->df_ectx_stack = &funcstack->fs_ga;
+ pt_dfunc->df_ectx_frame = ectx->ec_frame_idx - top;
+ }
+ }
+ }
+
+ return OK;
+ }
+
+ /*
* Return from the current function.
*/
! static int
func_return(ectx_T *ectx)
{
int idx;
! dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
! + ectx->ec_dfunc_idx;
! int argcount = ufunc_argcount(dfunc->df_ufunc);
! int top = ectx->ec_frame_idx - argcount;
// execution context goes one level up
estack_pop();
! if (handle_closure_in_use(ectx, TRUE) == FAIL)
! return FAIL;
// Clear the arguments.
! for (idx = top; idx < ectx->ec_frame_idx; ++idx)
! clear_tv(STACK_TV(idx));
!
! // Clear local variables and temp values, but not the return value.
! for (idx = ectx->ec_frame_idx + STACK_FRAME_SIZE;
! idx < ectx->ec_stack.ga_len - 1; ++idx)
clear_tv(STACK_TV(idx));
// Restore the previous frame.
! ectx->ec_dfunc_idx = STACK_TV(ectx->ec_frame_idx)->vval.v_number;
! ectx->ec_iidx = STACK_TV(ectx->ec_frame_idx + 1)->vval.v_number;
! ectx->ec_frame_idx = STACK_TV(ectx->ec_frame_idx + 2)->vval.v_number;
dfunc = ((dfunc_T *)def_functions.ga_data) + ectx->ec_dfunc_idx;
ectx->ec_instr = dfunc->df_instr;
***************
*** 282,287 ****
--- 370,377 ----
idx = ectx->ec_stack.ga_len - 1;
ectx->ec_stack.ga_len = top + 1;
*STACK_TV_BOT(-1) = *STACK_TV(idx);
+
+ return OK;
}
#undef STACK_TV
***************
*** 498,504 ****
{
ectx_T ectx; // execution context
int argc = argc_arg;
! int initial_frame_ptr;
typval_T *tv;
int idx;
int ret = FAIL;
--- 588,594 ----
{
ectx_T ectx; // execution context
int argc = argc_arg;
! int initial_frame_idx;
typval_T *tv;
int idx;
int ret = FAIL;
***************
*** 513,519 ****
#define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) +
ectx.ec_stack.ga_len + idx)
// Get pointer to a local variable on the stack. Negative for arguments.
! #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) +
ectx.ec_frame + STACK_FRAME_SIZE + idx)
// Like STACK_TV_VAR but use the outer scope
#define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) +
ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
--- 603,609 ----
#define STACK_TV_BOT(idx) (((typval_T *)ectx.ec_stack.ga_data) +
ectx.ec_stack.ga_len + idx)
// Get pointer to a local variable on the stack. Negative for arguments.
! #define STACK_TV_VAR(idx) (((typval_T *)ectx.ec_stack.ga_data) +
ectx.ec_frame_idx + STACK_FRAME_SIZE + idx)
// Like STACK_TV_VAR but use the outer scope
#define STACK_OUT_TV_VAR(idx) (((typval_T *)ectx.ec_outer_stack->ga_data) +
ectx.ec_outer_frame + STACK_FRAME_SIZE + idx)
***************
*** 562,569 ****
++ectx.ec_stack.ga_len;
// Frame pointer points to just after arguments.
! ectx.ec_frame = ectx.ec_stack.ga_len;
! initial_frame_ptr = ectx.ec_frame;
// dummy frame entries
for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
--- 652,659 ----
++ectx.ec_stack.ga_len;
// Frame pointer points to just after arguments.
! ectx.ec_frame_idx = ectx.ec_stack.ga_len;
! initial_frame_idx = ectx.ec_frame_idx;
// dummy frame entries
for (idx = 0; idx < STACK_FRAME_SIZE; ++idx)
***************
*** 573,585 ****
}
{
! // Reserve space for local variables.
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
! for (idx = 0; idx < dfunc->df_varcount; ++idx)
STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
! ectx.ec_stack.ga_len += dfunc->df_varcount;
ectx.ec_instr = dfunc->df_instr;
}
--- 663,676 ----
}
{
! // Reserve space for local variables and closure references.
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ufunc->uf_dfunc_idx;
+ int count = dfunc->df_varcount + dfunc->df_closure_count;
! for (idx = 0; idx < count; ++idx)
STACK_TV_VAR(idx)->v_type = VAR_UNKNOWN;
! ectx.ec_stack.ga_len += count;
ectx.ec_instr = dfunc->df_instr;
}
***************
*** 623,629 ****
// the current function.
if (trystack->ga_len > 0)
trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
! if (trycmd != NULL && trycmd->tcd_frame == ectx.ec_frame)
{
// jump to ":catch" or ":finally"
ectx.ec_in_catch = TRUE;
--- 714,720 ----
// the current function.
if (trystack->ga_len > 0)
trycmd = ((trycmd_T *)trystack->ga_data) + trystack->ga_len - 1;
! if (trycmd != NULL && trycmd->tcd_frame_idx == ectx.ec_frame_idx)
{
// jump to ":catch" or ":finally"
ectx.ec_in_catch = TRUE;
***************
*** 632,638 ****
else
{
// not inside try or need to return from current functions.
! if (ectx.ec_frame == initial_frame_ptr)
{
// At the toplevel we are done. Push a dummy return value.
if (ga_grow(&ectx.ec_stack, 1) == FAIL)
--- 723,729 ----
else
{
// not inside try or need to return from current functions.
! if (ectx.ec_frame_idx == initial_frame_idx)
{
// At the toplevel we are done. Push a dummy return value.
if (ga_grow(&ectx.ec_stack, 1) == FAIL)
***************
*** 642,651 ****
tv->vval.v_number = 0;
++ectx.ec_stack.ga_len;
need_rethrow = TRUE;
goto done;
}
! func_return(&ectx);
}
continue;
}
--- 733,745 ----
tv->vval.v_number = 0;
++ectx.ec_stack.ga_len;
need_rethrow = TRUE;
+ if (handle_closure_in_use(&ectx, FALSE) == FAIL)
+ goto failed;
goto done;
}
! if (func_return(&ectx) == FAIL)
! goto failed;
}
continue;
}
***************
*** 1073,1080 ****
}
--ectx.ec_stack.ga_len;
! di = find_var_in_ht(ht, 0,
! iptr->isn_arg.string + 2, TRUE);
if (di == NULL)
store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
else
--- 1167,1173 ----
}
--ectx.ec_stack.ga_len;
! di = find_var_in_ht(ht, 0, iptr->isn_arg.string + 2, TRUE);
if (di == NULL)
store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
else
***************
*** 1289,1295 ****
if (trystack->ga_len > 0)
trycmd = ((trycmd_T *)trystack->ga_data)
+ trystack->ga_len - 1;
! if (trycmd != NULL && trycmd->tcd_frame == ectx.ec_frame
&& trycmd->tcd_finally_idx != 0)
{
// jump to ":finally"
--- 1382,1389 ----
if (trystack->ga_len > 0)
trycmd = ((trycmd_T *)trystack->ga_data)
+ trystack->ga_len - 1;
! if (trycmd != NULL
! && trycmd->tcd_frame_idx == ectx.ec_frame_idx
&& trycmd->tcd_finally_idx != 0)
{
// jump to ":finally"
***************
*** 1300,1309 ****
{
// Restore previous function. If the frame pointer
// is zero then there is none and we are done.
! if (ectx.ec_frame == initial_frame_ptr)
goto done;
! func_return(&ectx);
}
}
break;
--- 1394,1408 ----
{
// Restore previous function. If the frame pointer
// is zero then there is none and we are done.
! if (ectx.ec_frame_idx == initial_frame_idx)
! {
! if (handle_closure_in_use(&ectx, FALSE) == FAIL)
! goto failed;
goto done;
+ }
! if (func_return(&ectx) == FAIL)
! goto failed;
}
}
break;
***************
*** 1312,1338 ****
case ISN_FUNCREF:
{
partial_T *pt = NULL;
! dfunc_T *dfunc;
pt = ALLOC_CLEAR_ONE(partial_T);
if (pt == NULL)
goto failed;
! dfunc = ((dfunc_T *)def_functions.ga_data)
! + iptr->isn_arg.number;
! pt->pt_func = dfunc->df_ufunc;
pt->pt_refcount = 1;
! ++dfunc->df_ufunc->uf_refcount;
! if (dfunc->df_ufunc->uf_flags & FC_CLOSURE)
{
! // Closure needs to find local variables in the current
! // stack.
! dfunc->df_ufunc->uf_ectx_stack = &ectx.ec_stack;
! dfunc->df_ufunc->uf_ectx_frame = ectx.ec_frame;
}
- if (ga_grow(&ectx.ec_stack, 1) == FAIL)
- goto failed;
tv = STACK_TV_BOT(0);
++ectx.ec_stack.ga_len;
tv->vval.v_partial = pt;
--- 1411,1459 ----
case ISN_FUNCREF:
{
partial_T *pt = NULL;
! dfunc_T *pt_dfunc;
pt = ALLOC_CLEAR_ONE(partial_T);
if (pt == NULL)
goto failed;
! if (ga_grow(&ectx.ec_stack, 1) == FAIL)
! {
! vim_free(pt);
! goto failed;
! }
! pt_dfunc = ((dfunc_T *)def_functions.ga_data)
! + iptr->isn_arg.funcref.fr_func;
! pt->pt_func = pt_dfunc->df_ufunc;
pt->pt_refcount = 1;
! ++pt_dfunc->df_ufunc->uf_refcount;
! if (pt_dfunc->df_ufunc->uf_flags & FC_CLOSURE)
{
! dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
! + ectx.ec_dfunc_idx;
!
! // The closure needs to find arguments and local
! // variables in the current stack.
! pt_dfunc->df_ectx_stack = &ectx.ec_stack;
! pt_dfunc->df_ectx_frame = ectx.ec_frame_idx;
!
! // If this function returns and the closure is still
! // used, we need to make a copy of the context
! // (arguments and local variables). Store a reference
! // to the partial so we can handle that.
! ++pt->pt_refcount;
! tv = STACK_TV_VAR(dfunc->df_varcount
! + iptr->isn_arg.funcref.fr_var_idx);
! if (tv->v_type == VAR_PARTIAL)
! {
! // TODO: use a garray_T on ectx.
! emsg("Multiple closures not supported yet");
! goto failed;
! }
! tv->v_type = VAR_PARTIAL;
! tv->vval.v_partial = pt;
}
tv = STACK_TV_BOT(0);
++ectx.ec_stack.ga_len;
tv->vval.v_partial = pt;
***************
*** 1410,1416 ****
+ ectx.ec_trystack.ga_len;
++ectx.ec_trystack.ga_len;
++trylevel;
! trycmd->tcd_frame = ectx.ec_frame;
trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch;
trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally;
trycmd->tcd_caught = FALSE;
--- 1531,1537 ----
+ ectx.ec_trystack.ga_len;
++ectx.ec_trystack.ga_len;
++trylevel;
! trycmd->tcd_frame_idx = ectx.ec_frame_idx;
trycmd->tcd_catch_idx = iptr->isn_arg.try.try_catch;
trycmd->tcd_finally_idx = iptr->isn_arg.try.try_finally;
trycmd->tcd_caught = FALSE;
***************
*** 1472,1481 ****
{
// Restore previous function. If the frame pointer
// is zero then there is none and we are done.
! if (ectx.ec_frame == initial_frame_ptr)
goto done;
! func_return(&ectx);
}
}
}
--- 1593,1607 ----
{
// Restore previous function. If the frame pointer
// is zero then there is none and we are done.
! if (ectx.ec_frame_idx == initial_frame_idx)
! {
! if (handle_closure_in_use(&ectx, FALSE) == FAIL)
! goto failed;
goto done;
+ }
! if (func_return(&ectx) == FAIL)
! goto failed;
}
}
}
***************
*** 1949,1960 ****
failed:
// When failed need to unwind the call stack.
! while (ectx.ec_frame != initial_frame_ptr)
func_return(&ectx);
failed_early:
current_sctx.sc_version = save_sc_version;
for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
clear_tv(STACK_TV(idx));
vim_free(ectx.ec_stack.ga_data);
vim_free(ectx.ec_trystack.ga_data);
return ret;
--- 2075,2089 ----
failed:
// When failed need to unwind the call stack.
! while (ectx.ec_frame_idx != initial_frame_idx)
func_return(&ectx);
failed_early:
current_sctx.sc_version = save_sc_version;
+
+ // Free all local variables, but not arguments.
for (idx = 0; idx < ectx.ec_stack.ga_len; ++idx)
clear_tv(STACK_TV(idx));
+
vim_free(ectx.ec_stack.ga_data);
vim_free(ectx.ec_trystack.ga_data);
return ret;
***************
*** 2309,2317 ****
case ISN_FUNCREF:
{
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
! + iptr->isn_arg.number;
! smsg("%4d FUNCREF %s", current, df->df_ufunc->uf_name);
}
break;
--- 2438,2447 ----
case ISN_FUNCREF:
{
dfunc_T *df = ((dfunc_T *)def_functions.ga_data)
! + iptr->isn_arg.funcref.fr_func;
! smsg("%4d FUNCREF %s $%d", current, df->df_ufunc->uf_name,
! iptr->isn_arg.funcref.fr_var_idx + df->df_varcount);
}
break;
*** ../vim-8.2.0678/src/testdir/test_vim9_func.vim 2020-05-01
19:29:05.006157706 +0200
--- src/testdir/test_vim9_func.vim 2020-05-02 15:45:50.533894513 +0200
***************
*** 650,653 ****
--- 650,665 ----
assert_equal('some more', RefFunc({s -> local .. s}))
enddef
+ def MakeRef()
+ let local = 'some '
+ g:Ref = {s -> local .. s}
+ enddef
+
+ def Test_closure_ref_after_return()
+ MakeRef()
+ assert_equal('some thing', g:Ref('thing'))
+ unlet g:Ref
+ enddef
+
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
*** ../vim-8.2.0678/src/version.c 2020-05-02 14:52:50.672723470 +0200
--- src/version.c 2020-05-02 17:47:42.880651929 +0200
***************
*** 748,749 ****
--- 748,751 ----
{ /* Add new patch number below this line */
+ /**/
+ 679,
/**/
--
Those who live by the sword get shot by those who don't.
/// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php
---
You received this message because you are subscribed to the Google Groups
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/vim_dev/202005021553.042FrkMN031796%40masaka.moolenaar.net.