Patch 9.0.0470
Problem: In a :def function all closures in a loop get the same variables.
Solution: When in a loop and a closure refers to a variable declared in the
loop, prepare for making a copy of variables for each closure.
Files: src/vim9.h, src/vim9cmds.c, src/vim9instr.c,
src/proto/
vim9instr.pro, src/vim9compile.c, src/vim9execute.c,
src/testdir/test_vim9_disassemble.vim
*** ../vim-9.0.0469/src/vim9.h 2022-09-07 16:48:41.183678514 +0100
--- src/vim9.h 2022-09-15 16:50:26.471818923 +0100
***************
*** 122,127 ****
--- 122,130 ----
// loop
ISN_FOR, // get next item from a list, uses isn_arg.forloop
+ ISN_WHILE, // jump if condition false, store funcref count, uses
+ // isn_arg.whileloop
+ ISN_ENDLOOP, // handle variables for closures, uses isn_arg.endloop
ISN_TRY, // add entry to ec_trystack, uses isn_arg.tryref
ISN_THROW, // pop value of stack, store in v:exception
***************
*** 240,245 ****
--- 243,249 ----
JUMP_ALWAYS,
JUMP_NEVER,
JUMP_IF_FALSE, // pop and jump if false
+ JUMP_WHILE_FALSE, // pop and jump if false for :while
JUMP_AND_KEEP_IF_TRUE, // jump if top of stack is truthy, drop if not
JUMP_IF_COND_TRUE, // jump if top of stack is true, drop if not
JUMP_IF_COND_FALSE, // jump if top of stack is false, drop if not
***************
*** 263,268 ****
--- 267,285 ----
int for_end; // position to jump to after done
} forloop_T;
+ // arguments to ISN_WHILE
+ typedef struct {
+ int while_funcref_idx; // variable index for funcref count
+ int while_end; // position to jump to after done
+ } whileloop_T;
+
+ // arguments to ISN_ENDLOOP
+ typedef struct {
+ short end_funcref_idx; // variable index of funcrefs.ga_len
+ short end_var_idx; // first variable declared in the loop
+ short end_var_count; // number of variables declared in the loop
+ } endloop_T;
+
// indirect arguments to ISN_TRY
typedef struct {
int try_catch; // position to jump to on throw
***************
*** 446,451 ****
--- 463,470 ----
jump_T jump;
jumparg_T jumparg;
forloop_T forloop;
+ whileloop_T whileloop;
+ endloop_T endloop;
try_T tryref;
trycont_T trycont;
cbfunc_T bfunc;
***************
*** 597,602 ****
--- 616,624 ----
typedef struct {
int ws_top_label; // instruction idx at WHILE
endlabel_T *ws_end_label; // instructions to set end
+ int ws_funcref_idx; // index of var that holds funcref count
+ int ws_local_count; // ctx_locals.ga_len at :while
+ int ws_closure_count; // ctx_closure_count at :while
} whilescope_T;
/*
***************
*** 605,610 ****
--- 627,635 ----
typedef struct {
int fs_top_label; // instruction idx at FOR
endlabel_T *fs_end_label; // break instructions
+ int fs_funcref_idx; // index of var that holds funcref count
+ int fs_local_count; // ctx_locals.ga_len at :for
+ int fs_closure_count; // ctx_closure_count at :for
} forscope_T;
/*
***************
*** 726,733 ****
garray_T ctx_locals; // currently visible local variables
! int ctx_has_closure; // set to one if a closure was created in
! // the function
skip_T ctx_skip;
scope_T *ctx_scope; // current scope, NULL at toplevel
--- 751,760 ----
garray_T ctx_locals; // currently visible local variables
! int ctx_has_closure; // set to one if a FUNCREF was used in the
! // function
! int ctx_closure_count; // incremented for each closure created in
! // the function.
skip_T ctx_skip;
scope_T *ctx_scope; // current scope, NULL at toplevel
*** ../vim-9.0.0469/src/vim9cmds.c 2022-09-08 20:49:16.504630443 +0100
--- src/vim9cmds.c 2022-09-15 17:18:34.306558902 +0100
***************
*** 278,287 ****
}
/*
! * generate a jump to the ":endif"/":endfor"/":endwhile"/":finally"/":endtry".
*/
static int
! compile_jump_to_end(endlabel_T **el, jumpwhen_T when, cctx_T *cctx)
{
garray_T *instr = &cctx->ctx_instr;
endlabel_T *endlabel = ALLOC_CLEAR_ONE(endlabel_T);
--- 278,292 ----
}
/*
! * Generate a jump to the ":endif"/":endfor"/":endwhile"/":finally"/":endtry".
! * "funcref_idx" is used for JUMP_WHILE_FALSE
*/
static int
! compile_jump_to_end(
! endlabel_T **el,
! jumpwhen_T when,
! int funcref_idx,
! cctx_T *cctx)
{
garray_T *instr = &cctx->ctx_instr;
endlabel_T *endlabel = ALLOC_CLEAR_ONE(endlabel_T);
***************
*** 292,298 ****
*el = endlabel;
endlabel->el_end_label = instr->ga_len;
! generate_JUMP(cctx, when, 0);
return OK;
}
--- 297,306 ----
*el = endlabel;
endlabel->el_end_label = instr->ga_len;
! if (when == JUMP_WHILE_FALSE)
! generate_WHILE(cctx, funcref_idx);
! else
! generate_JUMP(cctx, when, 0);
return OK;
}
***************
*** 564,570 ****
}
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
! JUMP_ALWAYS, cctx) == FAIL)
return NULL;
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
--- 572,578 ----
}
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
! JUMP_ALWAYS, 0, cctx) == FAIL)
return NULL;
// previous "if" or "elseif" jumps here
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
***************
*** 695,701 ****
{
if (!cctx->ctx_had_return
&& compile_jump_to_end(&scope->se_u.se_if.is_end_label,
! JUMP_ALWAYS, cctx) == FAIL)
return NULL;
}
--- 703,709 ----
{
if (!cctx->ctx_had_return
&& compile_jump_to_end(&scope->se_u.se_if.is_end_label,
! JUMP_ALWAYS, 0, cctx) == FAIL)
return NULL;
}
***************
*** 771,786 ****
* Compile "for var in expr":
*
* Produces instructions:
! * PUSHNR -1
! * STORE loop-idx Set index to -1
! * EVAL expr result of "expr" on top of stack
* top: FOR loop-idx, end Increment index, use list on bottom of stack
* - if beyond end, jump to "end"
* - otherwise get item from list and push it
* STORE var Store item in "var"
* ... body ...
! * JUMP top Jump back to repeat
! * end: DROP Drop the result of "expr"
*
* Compile "for [var1, var2] in expr" - as above, but instead of "STORE var":
* UNPACK 2 Split item in 2
--- 779,795 ----
* Compile "for var in expr":
*
* Produces instructions:
! * STORE -1 in loop-idx Set index to -1
! * EVAL expr Result of "expr" on top of stack
* top: FOR loop-idx, end Increment index, use list on bottom of stack
* - if beyond end, jump to "end"
* - otherwise get item from list and push it
+ * - store ec_funcrefs in var "loop-idx" + 1
* STORE var Store item in "var"
* ... body ...
! * ENDLOOP funcref-idx off count Only if closure uses local var
! * JUMP top Jump back to repeat
! * end: DROP Drop the result of "expr"
*
* Compile "for [var1, var2] in expr" - as above, but instead of "STORE var":
* UNPACK 2 Split item in 2
***************
*** 801,807 ****
--- 810,818 ----
size_t varlen;
garray_T *instr = &cctx->ctx_instr;
scope_T *scope;
+ forscope_T *forscope;
lvar_T *loop_lvar; // loop iteration variable
+ lvar_T *funcref_lvar;
lvar_T *var_lvar; // variable for "var"
type_T *vartype;
type_T *item_type = &t_any;
***************
*** 845,862 ****
scope = new_scope(cctx, FOR_SCOPE);
if (scope == NULL)
return NULL;
// Reserve a variable to store the loop iteration counter and initialize it
// to -1.
loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
if (loop_lvar == NULL)
{
- // out of memory
drop_scope(cctx);
! return NULL;
}
generate_STORENR(cctx, loop_lvar->lv_idx, -1);
// compile "expr", it remains on the stack until "endfor"
arg = p;
if (compile_expr0(&arg, cctx) == FAIL)
--- 856,883 ----
scope = new_scope(cctx, FOR_SCOPE);
if (scope == NULL)
return NULL;
+ forscope = &scope->se_u.se_for;
// Reserve a variable to store the loop iteration counter and initialize it
// to -1.
loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
if (loop_lvar == NULL)
{
drop_scope(cctx);
! return NULL; // out of memory
}
generate_STORENR(cctx, loop_lvar->lv_idx, -1);
+ // Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP.
+ // The variable index is always the loop var index plus one.
+ // It is not used when no closures are encountered, we don't know yet.
+ funcref_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
+ if (funcref_lvar == NULL)
+ {
+ drop_scope(cctx);
+ return NULL; // out of memory
+ }
+
// compile "expr", it remains on the stack until "endfor"
arg = p;
if (compile_expr0(&arg, cctx) == FAIL)
***************
*** 901,907 ****
generate_undo_cmdmods(cctx);
// "for_end" is set when ":endfor" is found
! scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
if (cctx->ctx_compile_type == CT_DEBUG)
{
--- 922,928 ----
generate_undo_cmdmods(cctx);
// "for_end" is set when ":endfor" is found
! forscope->fs_top_label = current_instr_idx(cctx);
if (cctx->ctx_compile_type == CT_DEBUG)
{
***************
*** 1019,1024 ****
--- 1040,1050 ----
arg = skipwhite(p);
vim_free(name);
}
+
+ forscope->fs_funcref_idx = funcref_lvar->lv_idx;
+ // remember the number of variables and closures, used in :endfor
+ forscope->fs_local_count = cctx->ctx_locals.ga_len;
+ forscope->fs_closure_count = cctx->ctx_closure_count;
}
return arg_end;
***************
*** 1030,1035 ****
--- 1056,1078 ----
}
/*
+ * At :endfor and :endwhile: Generate an ISN_ENDLOOP instruction if any
+ * variable was declared that could be used by a new closure.
+ */
+ static int
+ compile_loop_end(
+ int prev_local_count,
+ int prev_closure_count,
+ int funcref_idx,
+ cctx_T *cctx)
+ {
+ if (cctx->ctx_locals.ga_len > prev_local_count
+ && cctx->ctx_closure_count > prev_closure_count)
+ return generate_ENDLOOP(cctx, funcref_idx, prev_local_count);
+ return OK;
+ }
+
+ /*
* compile "endfor"
*/
char_u *
***************
*** 1052,1057 ****
--- 1095,1108 ----
cctx->ctx_scope = scope->se_outer;
if (cctx->ctx_skip != SKIP_YES)
{
+ // Handle the case that any local variables were declared that might be
+ // used in a closure.
+ if (compile_loop_end(forscope->fs_local_count,
+ forscope->fs_closure_count,
+ forscope->fs_funcref_idx,
+ cctx) == FAIL)
+ return NULL;
+
unwind_locals(cctx, scope->se_local_count);
// At end of ":for" scope jump back to the FOR instruction.
***************
*** 1080,1104 ****
* compile "while expr"
*
* Produces instructions:
! * top: EVAL expr Push result of "expr"
! * JUMP_IF_FALSE end jump if false
! * ... body ...
! * JUMP top Jump back to repeat
* end:
*
*/
char_u *
compile_while(char_u *arg, cctx_T *cctx)
{
! char_u *p = arg;
! scope_T *scope;
scope = new_scope(cctx, WHILE_SCOPE);
if (scope == NULL)
return NULL;
// "endwhile" jumps back here, one before when profiling or using cmdmods
! scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);
// compile "expr"
if (compile_expr0(&p, cctx) == FAIL)
--- 1131,1172 ----
* compile "while expr"
*
* Produces instructions:
! * top: EVAL expr Push result of "expr"
! * WHILE funcref-idx end Jump if false
! * ... body ...
! * ENDLOOP funcref-idx off count only if closure uses local var
! * JUMP top Jump back to repeat
* end:
*
*/
char_u *
compile_while(char_u *arg, cctx_T *cctx)
{
! char_u *p = arg;
! scope_T *scope;
! whilescope_T *whilescope;
! lvar_T *funcref_lvar;
scope = new_scope(cctx, WHILE_SCOPE);
if (scope == NULL)
return NULL;
+ whilescope = &scope->se_u.se_while;
// "endwhile" jumps back here, one before when profiling or using cmdmods
! whilescope->ws_top_label = current_instr_idx(cctx);
!
! // Reserve a variable to store ec_funcrefs.ga_len, used in ISN_ENDLOOP.
! // It is not used when no closures are encountered, we don't know yet.
! funcref_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
! if (funcref_lvar == NULL)
! {
! drop_scope(cctx);
! return NULL; // out of memory
! }
! whilescope->ws_funcref_idx = funcref_lvar->lv_idx;
! // remember the number of variables and closures, used in :endwhile
! whilescope->ws_local_count = cctx->ctx_locals.ga_len;
! whilescope->ws_closure_count = cctx->ctx_closure_count;
// compile "expr"
if (compile_expr0(&p, cctx) == FAIL)
***************
*** 1119,1126 ****
generate_undo_cmdmods(cctx);
// "while_end" is set when ":endwhile" is found
! if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
! JUMP_IF_FALSE, cctx) == FAIL)
return FAIL;
}
--- 1187,1194 ----
generate_undo_cmdmods(cctx);
// "while_end" is set when ":endwhile" is found
! if (compile_jump_to_end(&whilescope->ws_end_label,
! JUMP_WHILE_FALSE, funcref_lvar->lv_idx, cctx) == FAIL)
return FAIL;
}
***************
*** 1146,1151 ****
--- 1214,1229 ----
cctx->ctx_scope = scope->se_outer;
if (cctx->ctx_skip != SKIP_YES)
{
+ whilescope_T *whilescope = &scope->se_u.se_while;
+
+ // Handle the case that any local variables were declared that might be
+ // used in a closure.
+ if (compile_loop_end(whilescope->ws_local_count,
+ whilescope->ws_closure_count,
+ whilescope->ws_funcref_idx,
+ cctx) == FAIL)
+ return NULL;
+
unwind_locals(cctx, scope->se_local_count);
#ifdef FEAT_PROFILE
***************
*** 1250,1256 ****
// Jump to the end of the FOR or WHILE loop. The instruction index will be
// filled in later.
! if (compile_jump_to_end(el, JUMP_ALWAYS, cctx) == FAIL)
return FAIL;
return arg;
--- 1328,1334 ----
// Jump to the end of the FOR or WHILE loop. The instruction index will be
// filled in later.
! if (compile_jump_to_end(el, JUMP_ALWAYS, 0, cctx) == FAIL)
return FAIL;
return arg;
***************
*** 1397,1403 ****
#endif
// Jump from end of previous block to :finally or :endtry
if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
! JUMP_ALWAYS, cctx) == FAIL)
return NULL;
// End :try or :catch scope: set value in ISN_TRY instruction
--- 1475,1481 ----
#endif
// Jump from end of previous block to :finally or :endtry
if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
! JUMP_ALWAYS, 0, cctx) == FAIL)
return NULL;
// End :try or :catch scope: set value in ISN_TRY instruction
*** ../vim-9.0.0469/src/vim9instr.c 2022-09-08 19:51:39.734308338 +0100
--- src/vim9instr.c 2022-09-15 16:51:14.995683716 +0100
***************
*** 1284,1289 ****
--- 1284,1310 ----
}
/*
+ * Generate an ISN_WHILE instruction. Similar to ISN_JUMP for :while
+ */
+ int
+ generate_WHILE(cctx_T *cctx, int funcref_idx)
+ {
+ isn_T *isn;
+ garray_T *stack = &cctx->ctx_type_stack;
+
+ RETURN_OK_IF_SKIP(cctx);
+ if ((isn = generate_instr(cctx, ISN_WHILE)) == NULL)
+ return FAIL;
+ isn->isn_arg.whileloop.while_funcref_idx = funcref_idx;
+ isn->isn_arg.whileloop.while_end = 0; // filled in later
+
+ if (stack->ga_len > 0)
+ --stack->ga_len;
+
+ return OK;
+ }
+
+ /*
* Generate an ISN_JUMP_IF_ARG_SET instruction.
*/
int
***************
*** 1312,1317 ****
--- 1333,1357 ----
// type doesn't matter, will be stored next
return push_type_stack(cctx, &t_any);
}
+
+ int
+ generate_ENDLOOP(
+ cctx_T *cctx,
+ int funcref_idx,
+ int prev_local_count)
+ {
+ isn_T *isn;
+
+ RETURN_OK_IF_SKIP(cctx);
+ if ((isn = generate_instr(cctx, ISN_ENDLOOP)) == NULL)
+ return FAIL;
+ isn->isn_arg.endloop.end_funcref_idx = funcref_idx;
+ isn->isn_arg.endloop.end_var_idx = prev_local_count;
+ isn->isn_arg.endloop.end_var_count =
+ cctx->ctx_locals.ga_len - prev_local_count;
+ return OK;
+ }
+
/*
* Generate an ISN_TRYCONT instruction.
*/
***************
*** 2295,2300 ****
--- 2335,2341 ----
case ISN_ECHOERR:
case ISN_ECHOMSG:
case ISN_ECHOWINDOW:
+ case ISN_ENDLOOP:
case ISN_ENDTRY:
case ISN_EXECCONCAT:
case ISN_EXECUTE:
***************
*** 2341,2350 ****
case ISN_RETURN_VOID:
case ISN_SHUFFLE:
case ISN_SLICE:
case ISN_STORE:
case ISN_STOREINDEX:
case ISN_STORENR:
- case ISN_SOURCE:
case ISN_STOREOUTER:
case ISN_STORERANGE:
case ISN_STOREREG:
--- 2382,2391 ----
case ISN_RETURN_VOID:
case ISN_SHUFFLE:
case ISN_SLICE:
+ case ISN_SOURCE:
case ISN_STORE:
case ISN_STOREINDEX:
case ISN_STORENR:
case ISN_STOREOUTER:
case ISN_STORERANGE:
case ISN_STOREREG:
***************
*** 2357,2362 ****
--- 2398,2404 ----
case ISN_UNLETRANGE:
case ISN_UNPACK:
case ISN_USEDICT:
+ case ISN_WHILE:
// nothing allocated
break;
}
*** ../vim-9.0.0469/src/proto/
vim9instr.pro 2022-09-08 19:51:39.734308338 +0100
--- src/proto/
vim9instr.pro 2022-09-15 16:51:26.339652123 +0100
***************
*** 43,56 ****
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
int generate_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off);
int generate_FOR(cctx_T *cctx, int loop_idx);
int generate_TRYCONT(cctx_T *cctx, int levels, int where);
int check_internal_func_args(cctx_T *cctx, int func_idx, int argcount, int method_call, type2_T **argtypes, type2_T *shuffled_argtypes);
int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
int generate_LISTAPPEND(cctx_T *cctx);
int generate_BLOBAPPEND(cctx_T *cctx);
- int check_args_on_stack(cctx_T *cctx, ufunc_T *ufunc, int argcount);
int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
--- 43,57 ----
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
int generate_DEF(cctx_T *cctx, char_u *name, size_t len);
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
+ int generate_WHILE(cctx_T *cctx, int funcref_idx);
int generate_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off);
int generate_FOR(cctx_T *cctx, int loop_idx);
+ int generate_ENDLOOP(cctx_T *cctx, int funcref_idx, int prev_local_count);
int generate_TRYCONT(cctx_T *cctx, int levels, int where);
int check_internal_func_args(cctx_T *cctx, int func_idx, int argcount, int method_call, type2_T **argtypes, type2_T *shuffled_argtypes);
int generate_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
int generate_LISTAPPEND(cctx_T *cctx);
int generate_BLOBAPPEND(cctx_T *cctx);
int generate_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
int generate_UCALL(cctx_T *cctx, char_u *name, int argcount);
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
*** ../vim-9.0.0469/src/vim9compile.c 2022-09-14 00:30:47.077316538 +0100
--- src/vim9compile.c 2022-09-15 16:49:57.859898708 +0100
***************
*** 3449,3456 ****
--- 3449,3462 ----
}
dfunc->df_varcount = dfunc->df_var_names.ga_len;
dfunc->df_has_closure = cctx.ctx_has_closure;
+
if (cctx.ctx_outer_used)
+ {
ufunc->uf_flags |= FC_CLOSURE;
+ if (outer_cctx != NULL)
+ ++outer_cctx->ctx_closure_count;
+ }
+
ufunc->uf_def_status = UF_COMPILED;
}
*** ../vim-9.0.0469/src/vim9execute.c 2022-09-11 11:49:19.098228660 +0100
--- src/vim9execute.c 2022-09-15 17:03:23.905543150 +0100
***************
*** 504,510 ****
// - if needed: a counter for number of closures created in
// ectx->ec_funcrefs.
varcount = dfunc->df_varcount + dfunc->df_has_closure;
! if (GA_GROW_FAILS(&ectx->ec_stack, arg_to_add + STACK_FRAME_SIZE + varcount))
return FAIL;
// If depth of calling is getting too high, don't execute the function.
--- 504,511 ----
// - if needed: a counter for number of closures created in
// ectx->ec_funcrefs.
varcount = dfunc->df_varcount + dfunc->df_has_closure;
! if (GA_GROW_FAILS(&ectx->ec_stack,
! arg_to_add + STACK_FRAME_SIZE + varcount))
return FAIL;
// If depth of calling is getting too high, don't execute the function.
***************
*** 553,558 ****
--- 554,561 ----
{
typval_T *tv = STACK_TV_BOT(STACK_FRAME_SIZE + dfunc->df_varcount);
+ // Initialize the variable that counts how many closures were created.
+ // This is used in handle_closure_in_use().
tv->v_type = VAR_NUMBER;
tv->vval.v_number = 0;
}
***************
*** 1821,1828 ****
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
! // The closure may need to find arguments and local variables in the
! // current stack.
pt->pt_outer.out_stack = &ectx->ec_stack;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
if (ectx->ec_outer_ref != NULL)
--- 1824,1831 ----
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
+ ectx->ec_dfunc_idx;
! // The closure may need to find arguments and local variables of the
! // current function in the stack.
pt->pt_outer.out_stack = &ectx->ec_stack;
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
if (ectx->ec_outer_ref != NULL)
***************
*** 1836,1843 ****
}
}
! // If this function returns and the closure is still being 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.
if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
{
--- 1839,1847 ----
}
}
! // If the function currently executing returns and the closure is still
! // being referenced, we need to make a copy of the context (arguments
! // and local variables) so that the closure can use it later.
// Store a reference to the partial so we can handle that.
if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
{
***************
*** 2477,2482 ****
--- 2481,2487 ----
execute_for(isn_T *iptr, ectx_T *ectx)
{
typval_T *tv;
+ int jump = FALSE;
typval_T *ltv = STACK_TV_BOT(-1);
typval_T *idxtv =
STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
***************
*** 2492,2500 ****
if (list == NULL
|| idxtv->vval.v_number >= list->lv_len)
{
! // past the end of the list, jump to "endfor"
! ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
! may_restore_cmdmod(&ectx->ec_funclocal);
}
else if (list->lv_first == &range_list_item)
{
--- 2497,2503 ----
if (list == NULL
|| idxtv->vval.v_number >= list->lv_len)
{
! jump = TRUE;
}
else if (list->lv_first == &range_list_item)
{
***************
*** 2524,2532 ****
++idxtv->vval.v_number;
if (str == NULL || str[idxtv->vval.v_number] == NUL)
{
! // past the end of the string, jump to "endfor"
! ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
! may_restore_cmdmod(&ectx->ec_funclocal);
}
else
{
--- 2527,2533 ----
++idxtv->vval.v_number;
if (str == NULL || str[idxtv->vval.v_number] == NUL)
{
! jump = TRUE;
}
else
{
***************
*** 2557,2568 ****
// The index is for the previous byte.
++idxtv->vval.v_number;
! if (blob == NULL
! || idxtv->vval.v_number >= blob_len(blob))
{
! // past the end of the blob, jump to "endfor"
! ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
! may_restore_cmdmod(&ectx->ec_funclocal);
}
else
{
--- 2558,2566 ----
// The index is for the previous byte.
++idxtv->vval.v_number;
! if (blob == NULL || idxtv->vval.v_number >= blob_len(blob))
{
! jump = TRUE;
}
else
{
***************
*** 2580,2585 ****
--- 2578,2610 ----
vartype_name(ltv->v_type));
return FAIL;
}
+
+ if (jump)
+ {
+ // past the end of the list/string/blob, jump to "endfor"
+ ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
+ may_restore_cmdmod(&ectx->ec_funclocal);
+ }
+ else
+ {
+ // Store the current number of funcrefs, this may be used in
+ // ISN_LOOPEND. The variable index is always one more than the loop
+ // variable index.
+ tv = STACK_TV_VAR(iptr->isn_arg.forloop.for_idx + 1);
+ tv->vval.v_number = ectx->ec_funcrefs.ga_len;
+ }
+
+ return OK;
+ }
+
+ /*
+ * End of a for or while loop: Handle any variables used by a closure.
+ */
+ static int
+ execute_endloop(isn_T *iptr UNUSED, ectx_T *ectx UNUSED)
+ {
+ // TODO
+
return OK;
}
***************
*** 3989,3994 ****
--- 4014,4044 ----
}
break;
+ // "while": jump to end if a condition is false
+ case ISN_WHILE:
+ {
+ int error = FALSE;
+ int jump = TRUE;
+
+ tv = STACK_TV_BOT(-1);
+ SOURCING_LNUM = iptr->isn_lnum;
+ jump = !tv_get_bool_chk(tv, &error);
+ if (error)
+ goto on_error;
+ // drop the value from the stack
+ clear_tv(tv);
+ --ectx->ec_stack.ga_len;
+ if (jump)
+ ectx->ec_iidx = iptr->isn_arg.whileloop.while_end;
+
+ // Store the current funccal count, may be used by
+ // ISN_LOOPEND later
+ tv = STACK_TV_VAR(
+ iptr->isn_arg.whileloop.while_funcref_idx);
+ tv->vval.v_number = ectx->ec_funcrefs.ga_len;
+ }
+ break;
+
// Jump if an argument with a default value was already set and not
// v:none.
case ISN_JUMP_IF_ARG_SET:
***************
*** 4005,4010 ****
--- 4055,4066 ----
goto theend;
break;
+ // end of a for or while loop
+ case ISN_ENDLOOP:
+ if (execute_endloop(iptr, ectx) == FAIL)
+ goto theend;
+ break;
+
// start of ":try" block
case ISN_TRY:
{
***************
*** 6185,6190 ****
--- 6241,6249 ----
case JUMP_IF_FALSE:
when = "JUMP_IF_FALSE";
break;
+ case JUMP_WHILE_FALSE:
+ when = "JUMP_WHILE_FALSE"; // unused
+ break;
case JUMP_IF_COND_FALSE:
when = "JUMP_IF_COND_FALSE";
break;
***************
*** 6212,6217 ****
--- 6271,6297 ----
}
break;
+ case ISN_ENDLOOP:
+ {
+ endloop_T *endloop = &iptr->isn_arg.endloop;
+
+ smsg("%s%4d ENDLOOP $%d save $%d - $%d", pfx, current,
+ endloop->end_funcref_idx,
+ endloop->end_var_idx,
+ endloop->end_var_idx + endloop->end_var_count - 1);
+ }
+ break;
+
+ case ISN_WHILE:
+ {
+ whileloop_T *whileloop = &iptr->isn_arg.whileloop;
+
+ smsg("%s%4d WHILE $%d -> %d", pfx, current,
+ whileloop->while_funcref_idx,
+ whileloop->while_end);
+ }
+ break;
+
case ISN_TRY:
{
try_T *try = &iptr->isn_arg.tryref;
*** ../vim-9.0.0469/src/testdir/test_vim9_disassemble.vim 2022-09-03 21:35:50.188158217 +0100
--- src/testdir/test_vim9_disassemble.vim 2022-09-15 15:15:39.515207696 +0100
***************
*** 1466,1482 ****
'\d NEWLIST size 0\_s*' ..
'\d SETTYPE list<number>\_s*' ..
'\d STORE $0\_s*' ..
'for i in range(3)\_s*' ..
'\d STORE -1 in $1\_s*' ..
'\d PUSHNR 3\_s*' ..
'\d BCALL range(argc 1)\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
! '\d STORE $2\_s*' ..
'res->add(i)\_s*' ..
'\d LOAD $0\_s*' ..
! '\d LOAD $2\_s*' ..
'\d\+ LISTAPPEND\_s*' ..
'\d\+ DROP\_s*' ..
'endfor\_s*' ..
'\d\+ JUMP -> \d\+\_s*' ..
'\d\+ DROP',
--- 1466,1485 ----
'\d NEWLIST size 0\_s*' ..
'\d SETTYPE list<number>\_s*' ..
'\d STORE $0\_s*' ..
+
'for i in range(3)\_s*' ..
'\d STORE -1 in $1\_s*' ..
'\d PUSHNR 3\_s*' ..
'\d BCALL range(argc 1)\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
! '\d STORE $3\_s*' ..
!
'res->add(i)\_s*' ..
'\d LOAD $0\_s*' ..
! '\d LOAD $3\_s*' ..
'\d\+ LISTAPPEND\_s*' ..
'\d\+ DROP\_s*' ..
+
'endfor\_s*' ..
'\d\+ JUMP -> \d\+\_s*' ..
'\d\+ DROP',
***************
*** 1498,1518 ****
'var res = ""\_s*' ..
'\d PUSHS ""\_s*' ..
'\d STORE $0\_s*' ..
'for str in eval(''\["one", "two"\]'')\_s*' ..
'\d STORE -1 in $1\_s*' ..
'\d PUSHS "\["one", "two"\]"\_s*' ..
'\d BCALL eval(argc 1)\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
! '\d STORE $2\_s*' ..
'res ..= str\_s*' ..
'\d\+ LOAD $0\_s*' ..
! '\d\+ LOAD $2\_s*' ..
'\d 2STRING_ANY stack\[-1\]\_s*' ..
'\d\+ CONCAT size 2\_s*' ..
'\d\+ STORE $0\_s*' ..
'endfor\_s*' ..
'\d\+ JUMP -> 5\_s*' ..
'\d\+ DROP\_s*' ..
'return res\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ RETURN',
--- 1501,1525 ----
'var res = ""\_s*' ..
'\d PUSHS ""\_s*' ..
'\d STORE $0\_s*' ..
+
'for str in eval(''\["one", "two"\]'')\_s*' ..
'\d STORE -1 in $1\_s*' ..
'\d PUSHS "\["one", "two"\]"\_s*' ..
'\d BCALL eval(argc 1)\_s*' ..
'\d FOR $1 -> \d\+\_s*' ..
! '\d STORE $3\_s*' ..
!
'res ..= str\_s*' ..
'\d\+ LOAD $0\_s*' ..
! '\d\+ LOAD $3\_s*' ..
'\d 2STRING_ANY stack\[-1\]\_s*' ..
'\d\+ CONCAT size 2\_s*' ..
'\d\+ STORE $0\_s*' ..
+
'endfor\_s*' ..
'\d\+ JUMP -> 5\_s*' ..
'\d\+ DROP\_s*' ..
+
'return res\_s*' ..
'\d\+ LOAD $0\_s*' ..
'\d\+ RETURN',
***************
*** 1539,1550 ****
'\d\+ NEWLIST size 2\_s*' ..
'\d\+ FOR $0 -> 16\_s*' ..
'\d\+ UNPACK 2\_s*' ..
- '\d\+ STORE $1\_s*' ..
'\d\+ STORE $2\_s*' ..
'echo x1 x2\_s*' ..
- '\d\+ LOAD $1\_s*' ..
'\d\+ LOAD $2\_s*' ..
'\d\+ ECHO 2\_s*' ..
'endfor\_s*' ..
'\d\+ JUMP -> 8\_s*' ..
'\d\+ DROP\_s*' ..
--- 1546,1559 ----
'\d\+ NEWLIST size 2\_s*' ..
'\d\+ FOR $0 -> 16\_s*' ..
'\d\+ UNPACK 2\_s*' ..
'\d\+ STORE $2\_s*' ..
+ '\d\+ STORE $3\_s*' ..
+
'echo x1 x2\_s*' ..
'\d\+ LOAD $2\_s*' ..
+ '\d\+ LOAD $3\_s*' ..
'\d\+ ECHO 2\_s*' ..
+
'endfor\_s*' ..
'\d\+ JUMP -> 8\_s*' ..
'\d\+ DROP\_s*' ..
***************
*** 1576,1607 ****
'2 PUSHNR 2\_s*' ..
'3 NEWLIST size 2\_s*' ..
'4 FOR $0 -> 22\_s*' ..
! '5 STORE $1\_s*' ..
'try\_s*' ..
'6 TRY catch -> 17, endtry -> 20\_s*' ..
'echo "ok"\_s*' ..
'7 PUSHS "ok"\_s*' ..
'8 ECHO 1\_s*' ..
'try\_s*' ..
'9 TRY catch -> 13, endtry -> 15\_s*' ..
'echo "deeper"\_s*' ..
'10 PUSHS "deeper"\_s*' ..
'11 ECHO 1\_s*' ..
'catch\_s*' ..
'12 JUMP -> 15\_s*' ..
'13 CATCH\_s*' ..
'continue\_s*' ..
'14 TRY-CONTINUE 2 levels -> 4\_s*' ..
'endtry\_s*' ..
'15 ENDTRY\_s*' ..
'catch\_s*' ..
'16 JUMP -> 20\_s*' ..
'17 CATCH\_s*' ..
'echo "not ok"\_s*' ..
'18 PUSHS "not ok"\_s*' ..
'19 ECHO 1\_s*' ..
'endtry\_s*' ..
'20 ENDTRY\_s*' ..
'endfor\_s*' ..
'21 JUMP -> 4\_s*' ..
'\d\+ DROP\_s*' ..
--- 1585,1627 ----
'2 PUSHNR 2\_s*' ..
'3 NEWLIST size 2\_s*' ..
'4 FOR $0 -> 22\_s*' ..
! '5 STORE $2\_s*' ..
!
'try\_s*' ..
'6 TRY catch -> 17, endtry -> 20\_s*' ..
+
'echo "ok"\_s*' ..
'7 PUSHS "ok"\_s*' ..
'8 ECHO 1\_s*' ..
+
'try\_s*' ..
'9 TRY catch -> 13, endtry -> 15\_s*' ..
+
'echo "deeper"\_s*' ..
'10 PUSHS "deeper"\_s*' ..
'11 ECHO 1\_s*' ..
+
'catch\_s*' ..
'12 JUMP -> 15\_s*' ..
'13 CATCH\_s*' ..
+
'continue\_s*' ..
'14 TRY-CONTINUE 2 levels -> 4\_s*' ..
+
'endtry\_s*' ..
'15 ENDTRY\_s*' ..
+
'catch\_s*' ..
'16 JUMP -> 20\_s*' ..
'17 CATCH\_s*' ..
+
'echo "not ok"\_s*' ..
'18 PUSHS "not ok"\_s*' ..
'19 ECHO 1\_s*' ..
+
'endtry\_s*' ..
'20 ENDTRY\_s*' ..
+
'endfor\_s*' ..
'21 JUMP -> 4\_s*' ..
'\d\+ DROP\_s*' ..
***************
*** 2478,2484 ****
'\d NEWLIST size 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
'5 FOR $0 -> 8\_s*' ..
! '\d STORE $1\_s*' ..
'endfor\_s*' ..
'\d JUMP -> 5\_s*' ..
'8 DROP\_s*' ..
--- 2498,2505 ----
'\d NEWLIST size 1\_s*' ..
'\d CMDMOD_REV\_s*' ..
'5 FOR $0 -> 8\_s*' ..
! '\d STORE $2\_s*' ..
!
'endfor\_s*' ..
'\d JUMP -> 5\_s*' ..
'8 DROP\_s*' ..
***************
*** 2499,2505 ****
'\d LOADG g:not\_s*' ..
'\d COND2BOOL\_s*' ..
'\d CMDMOD_REV\_s*' ..
! '\d JUMP_IF_FALSE -> 6\_s*' ..
'endwhile\_s*' ..
'\d JUMP -> 0\_s*' ..
--- 2520,2526 ----
'\d LOADG g:not\_s*' ..
'\d COND2BOOL\_s*' ..
'\d CMDMOD_REV\_s*' ..
! '\d WHILE $0 -> 6\_s*' ..
'endwhile\_s*' ..
'\d JUMP -> 0\_s*' ..
***************
*** 2691,2707 ****
'4 STORE -1 in $0\_s*' ..
'5 PUSHNR 0\_s*' ..
'6 NEWLIST size 1\_s*' ..
! '7 DEBUG line 2-2 varcount 2\_s*' ..
'8 FOR $0 -> 15\_s*' ..
! '9 STORE $1\_s*' ..
'echo a\_s*' ..
! '10 DEBUG line 3-3 varcount 2\_s*' ..
! '11 LOAD $1\_s*' ..
'12 ECHO 1\_s*' ..
'endfor\_s*' ..
! '13 DEBUG line 4-4 varcount 2\_s*' ..
'14 JUMP -> 7\_s*' ..
'15 DROP\_s*' ..
'16 RETURN void*',
--- 2712,2728 ----
'4 STORE -1 in $0\_s*' ..
'5 PUSHNR 0\_s*' ..
'6 NEWLIST size 1\_s*' ..
! '7 DEBUG line 2-2 varcount 3\_s*' ..
'8 FOR $0 -> 15\_s*' ..
! '9 STORE $2\_s*' ..
'echo a\_s*' ..
! '10 DEBUG line 3-3 varcount 3\_s*' ..
! '11 LOAD $2\_s*' ..
'12 ECHO 1\_s*' ..
'endfor\_s*' ..
! '13 DEBUG line 4-4 varcount 3\_s*' ..
'14 JUMP -> 7\_s*' ..
'15 DROP\_s*' ..
'16 RETURN void*',
*** ../vim-9.0.0469/src/version.c 2022-09-15 12:43:20.476321981 +0100
--- src/version.c 2022-09-15 16:52:28.819478213 +0100
***************
*** 705,706 ****
--- 705,708 ----
{ /* Add new patch number below this line */
+ /**/
+ 470,
/**/
--
hundred-and-one symptoms of being an internet addict:
84. Books in your bookcase bear the names Bongo, WinSock and Inside OLE
/// Bram Moolenaar -- Br...@Moolenaar.net --
http://www.Moolenaar.net \\\
/// \\\
\\\ sponsor Vim, vote for features --
http://www.Vim.org/sponsor/ ///
\\\ help me help AIDS victims --
http://ICCF-Holland.org ///