diff options
author | Bram Moolenaar <Bram@vim.org> | 2022-09-15 17:19:37 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2022-09-15 17:19:37 +0100 |
commit | b46c083a5ed9e0c4ac5f3aec577946dcbe8c9dc5 (patch) | |
tree | f91c0168ac87183c5df558840b9cf920d18df558 /src/vim9execute.c | |
parent | 3735f11050616652525bf80b4fbcb2b3bfeab113 (diff) | |
download | vim-git-b46c083a5ed9e0c4ac5f3aec577946dcbe8c9dc5.tar.gz |
patch 9.0.0470: in :def function all closures in loop get the same variablesv9.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.
Diffstat (limited to 'src/vim9execute.c')
-rw-r--r-- | src/vim9execute.c | 112 |
1 files changed, 96 insertions, 16 deletions
diff --git a/src/vim9execute.c b/src/vim9execute.c index cbf9af7ad..a1c2f8b27 100644 --- a/src/vim9execute.c +++ b/src/vim9execute.c @@ -504,7 +504,8 @@ call_dfunc( // - 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)) + 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,6 +554,8 @@ call_dfunc( { 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,8 +1824,8 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx) 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. + // 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,8 +1839,9 @@ fill_partial_and_closure(partial_T *pt, ufunc_T *ufunc, ectx_T *ectx) } } - // If this function returns and the closure is still being used, we - // need to make a copy of the context (arguments and local variables). + // 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,6 +2481,7 @@ execute_unletrange(isn_T *iptr, ectx_T *ectx) 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,9 +2497,7 @@ execute_for(isn_T *iptr, ectx_T *ectx) 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); + jump = TRUE; } else if (list->lv_first == &range_list_item) { @@ -2524,9 +2527,7 @@ execute_for(isn_T *iptr, ectx_T *ectx) ++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); + jump = TRUE; } else { @@ -2557,12 +2558,9 @@ execute_for(isn_T *iptr, ectx_T *ectx) // The index is for the previous byte. ++idxtv->vval.v_number; - if (blob == NULL - || idxtv->vval.v_number >= blob_len(blob)) + 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); + jump = TRUE; } else { @@ -2580,6 +2578,33 @@ execute_for(isn_T *iptr, ectx_T *ectx) 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,6 +4014,31 @@ exec_instructions(ectx_T *ectx) } 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,6 +4055,12 @@ exec_instructions(ectx_T *ectx) 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,6 +6241,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) 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,6 +6271,27 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc) } 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; |