mirror of
https://github.com/vim/vim.git
synced 2025-09-24 03:44:06 -04:00
patch 9.0.0470: in :def function all closures in loop get the same variables
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.
This commit is contained in:
@@ -43,14 +43,15 @@ int generate_FUNCREF(cctx_T *cctx, ufunc_T *ufunc, isn_T **isnp);
|
|||||||
int generate_NEWFUNC(cctx_T *cctx, char_u *lambda_name, char_u *func_name);
|
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_DEF(cctx_T *cctx, char_u *name, size_t len);
|
||||||
int generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where);
|
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_JUMP_IF_ARG_SET(cctx_T *cctx, int arg_off);
|
||||||
int generate_FOR(cctx_T *cctx, int loop_idx);
|
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 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 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_BCALL(cctx_T *cctx, int func_idx, int argcount, int method_call);
|
||||||
int generate_LISTAPPEND(cctx_T *cctx);
|
int generate_LISTAPPEND(cctx_T *cctx);
|
||||||
int generate_BLOBAPPEND(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_CALL(cctx_T *cctx, ufunc_T *ufunc, int pushed_argcount);
|
||||||
int generate_UCALL(cctx_T *cctx, char_u *name, int 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);
|
int check_func_args_from_type(cctx_T *cctx, type_T *type, int argcount, int at_top, char_u *name);
|
||||||
|
@@ -1466,17 +1466,20 @@ def Test_disassemble_for_loop()
|
|||||||
'\d NEWLIST size 0\_s*' ..
|
'\d NEWLIST size 0\_s*' ..
|
||||||
'\d SETTYPE list<number>\_s*' ..
|
'\d SETTYPE list<number>\_s*' ..
|
||||||
'\d STORE $0\_s*' ..
|
'\d STORE $0\_s*' ..
|
||||||
|
|
||||||
'for i in range(3)\_s*' ..
|
'for i in range(3)\_s*' ..
|
||||||
'\d STORE -1 in $1\_s*' ..
|
'\d STORE -1 in $1\_s*' ..
|
||||||
'\d PUSHNR 3\_s*' ..
|
'\d PUSHNR 3\_s*' ..
|
||||||
'\d BCALL range(argc 1)\_s*' ..
|
'\d BCALL range(argc 1)\_s*' ..
|
||||||
'\d FOR $1 -> \d\+\_s*' ..
|
'\d FOR $1 -> \d\+\_s*' ..
|
||||||
'\d STORE $2\_s*' ..
|
'\d STORE $3\_s*' ..
|
||||||
|
|
||||||
'res->add(i)\_s*' ..
|
'res->add(i)\_s*' ..
|
||||||
'\d LOAD $0\_s*' ..
|
'\d LOAD $0\_s*' ..
|
||||||
'\d LOAD $2\_s*' ..
|
'\d LOAD $3\_s*' ..
|
||||||
'\d\+ LISTAPPEND\_s*' ..
|
'\d\+ LISTAPPEND\_s*' ..
|
||||||
'\d\+ DROP\_s*' ..
|
'\d\+ DROP\_s*' ..
|
||||||
|
|
||||||
'endfor\_s*' ..
|
'endfor\_s*' ..
|
||||||
'\d\+ JUMP -> \d\+\_s*' ..
|
'\d\+ JUMP -> \d\+\_s*' ..
|
||||||
'\d\+ DROP',
|
'\d\+ DROP',
|
||||||
@@ -1498,21 +1501,25 @@ def Test_disassemble_for_loop_eval()
|
|||||||
'var res = ""\_s*' ..
|
'var res = ""\_s*' ..
|
||||||
'\d PUSHS ""\_s*' ..
|
'\d PUSHS ""\_s*' ..
|
||||||
'\d STORE $0\_s*' ..
|
'\d STORE $0\_s*' ..
|
||||||
|
|
||||||
'for str in eval(''\["one", "two"\]'')\_s*' ..
|
'for str in eval(''\["one", "two"\]'')\_s*' ..
|
||||||
'\d STORE -1 in $1\_s*' ..
|
'\d STORE -1 in $1\_s*' ..
|
||||||
'\d PUSHS "\["one", "two"\]"\_s*' ..
|
'\d PUSHS "\["one", "two"\]"\_s*' ..
|
||||||
'\d BCALL eval(argc 1)\_s*' ..
|
'\d BCALL eval(argc 1)\_s*' ..
|
||||||
'\d FOR $1 -> \d\+\_s*' ..
|
'\d FOR $1 -> \d\+\_s*' ..
|
||||||
'\d STORE $2\_s*' ..
|
'\d STORE $3\_s*' ..
|
||||||
|
|
||||||
'res ..= str\_s*' ..
|
'res ..= str\_s*' ..
|
||||||
'\d\+ LOAD $0\_s*' ..
|
'\d\+ LOAD $0\_s*' ..
|
||||||
'\d\+ LOAD $2\_s*' ..
|
'\d\+ LOAD $3\_s*' ..
|
||||||
'\d 2STRING_ANY stack\[-1\]\_s*' ..
|
'\d 2STRING_ANY stack\[-1\]\_s*' ..
|
||||||
'\d\+ CONCAT size 2\_s*' ..
|
'\d\+ CONCAT size 2\_s*' ..
|
||||||
'\d\+ STORE $0\_s*' ..
|
'\d\+ STORE $0\_s*' ..
|
||||||
|
|
||||||
'endfor\_s*' ..
|
'endfor\_s*' ..
|
||||||
'\d\+ JUMP -> 5\_s*' ..
|
'\d\+ JUMP -> 5\_s*' ..
|
||||||
'\d\+ DROP\_s*' ..
|
'\d\+ DROP\_s*' ..
|
||||||
|
|
||||||
'return res\_s*' ..
|
'return res\_s*' ..
|
||||||
'\d\+ LOAD $0\_s*' ..
|
'\d\+ LOAD $0\_s*' ..
|
||||||
'\d\+ RETURN',
|
'\d\+ RETURN',
|
||||||
@@ -1539,12 +1546,14 @@ def Test_disassemble_for_loop_unpack()
|
|||||||
'\d\+ NEWLIST size 2\_s*' ..
|
'\d\+ NEWLIST size 2\_s*' ..
|
||||||
'\d\+ FOR $0 -> 16\_s*' ..
|
'\d\+ FOR $0 -> 16\_s*' ..
|
||||||
'\d\+ UNPACK 2\_s*' ..
|
'\d\+ UNPACK 2\_s*' ..
|
||||||
'\d\+ STORE $1\_s*' ..
|
|
||||||
'\d\+ STORE $2\_s*' ..
|
'\d\+ STORE $2\_s*' ..
|
||||||
|
'\d\+ STORE $3\_s*' ..
|
||||||
|
|
||||||
'echo x1 x2\_s*' ..
|
'echo x1 x2\_s*' ..
|
||||||
'\d\+ LOAD $1\_s*' ..
|
|
||||||
'\d\+ LOAD $2\_s*' ..
|
'\d\+ LOAD $2\_s*' ..
|
||||||
|
'\d\+ LOAD $3\_s*' ..
|
||||||
'\d\+ ECHO 2\_s*' ..
|
'\d\+ ECHO 2\_s*' ..
|
||||||
|
|
||||||
'endfor\_s*' ..
|
'endfor\_s*' ..
|
||||||
'\d\+ JUMP -> 8\_s*' ..
|
'\d\+ JUMP -> 8\_s*' ..
|
||||||
'\d\+ DROP\_s*' ..
|
'\d\+ DROP\_s*' ..
|
||||||
@@ -1576,32 +1585,43 @@ def Test_disassemble_for_loop_continue()
|
|||||||
'2 PUSHNR 2\_s*' ..
|
'2 PUSHNR 2\_s*' ..
|
||||||
'3 NEWLIST size 2\_s*' ..
|
'3 NEWLIST size 2\_s*' ..
|
||||||
'4 FOR $0 -> 22\_s*' ..
|
'4 FOR $0 -> 22\_s*' ..
|
||||||
'5 STORE $1\_s*' ..
|
'5 STORE $2\_s*' ..
|
||||||
|
|
||||||
'try\_s*' ..
|
'try\_s*' ..
|
||||||
'6 TRY catch -> 17, endtry -> 20\_s*' ..
|
'6 TRY catch -> 17, endtry -> 20\_s*' ..
|
||||||
|
|
||||||
'echo "ok"\_s*' ..
|
'echo "ok"\_s*' ..
|
||||||
'7 PUSHS "ok"\_s*' ..
|
'7 PUSHS "ok"\_s*' ..
|
||||||
'8 ECHO 1\_s*' ..
|
'8 ECHO 1\_s*' ..
|
||||||
|
|
||||||
'try\_s*' ..
|
'try\_s*' ..
|
||||||
'9 TRY catch -> 13, endtry -> 15\_s*' ..
|
'9 TRY catch -> 13, endtry -> 15\_s*' ..
|
||||||
|
|
||||||
'echo "deeper"\_s*' ..
|
'echo "deeper"\_s*' ..
|
||||||
'10 PUSHS "deeper"\_s*' ..
|
'10 PUSHS "deeper"\_s*' ..
|
||||||
'11 ECHO 1\_s*' ..
|
'11 ECHO 1\_s*' ..
|
||||||
|
|
||||||
'catch\_s*' ..
|
'catch\_s*' ..
|
||||||
'12 JUMP -> 15\_s*' ..
|
'12 JUMP -> 15\_s*' ..
|
||||||
'13 CATCH\_s*' ..
|
'13 CATCH\_s*' ..
|
||||||
|
|
||||||
'continue\_s*' ..
|
'continue\_s*' ..
|
||||||
'14 TRY-CONTINUE 2 levels -> 4\_s*' ..
|
'14 TRY-CONTINUE 2 levels -> 4\_s*' ..
|
||||||
|
|
||||||
'endtry\_s*' ..
|
'endtry\_s*' ..
|
||||||
'15 ENDTRY\_s*' ..
|
'15 ENDTRY\_s*' ..
|
||||||
|
|
||||||
'catch\_s*' ..
|
'catch\_s*' ..
|
||||||
'16 JUMP -> 20\_s*' ..
|
'16 JUMP -> 20\_s*' ..
|
||||||
'17 CATCH\_s*' ..
|
'17 CATCH\_s*' ..
|
||||||
|
|
||||||
'echo "not ok"\_s*' ..
|
'echo "not ok"\_s*' ..
|
||||||
'18 PUSHS "not ok"\_s*' ..
|
'18 PUSHS "not ok"\_s*' ..
|
||||||
'19 ECHO 1\_s*' ..
|
'19 ECHO 1\_s*' ..
|
||||||
|
|
||||||
'endtry\_s*' ..
|
'endtry\_s*' ..
|
||||||
'20 ENDTRY\_s*' ..
|
'20 ENDTRY\_s*' ..
|
||||||
|
|
||||||
'endfor\_s*' ..
|
'endfor\_s*' ..
|
||||||
'21 JUMP -> 4\_s*' ..
|
'21 JUMP -> 4\_s*' ..
|
||||||
'\d\+ DROP\_s*' ..
|
'\d\+ DROP\_s*' ..
|
||||||
@@ -2478,7 +2498,8 @@ def Test_silent_for()
|
|||||||
'\d NEWLIST size 1\_s*' ..
|
'\d NEWLIST size 1\_s*' ..
|
||||||
'\d CMDMOD_REV\_s*' ..
|
'\d CMDMOD_REV\_s*' ..
|
||||||
'5 FOR $0 -> 8\_s*' ..
|
'5 FOR $0 -> 8\_s*' ..
|
||||||
'\d STORE $1\_s*' ..
|
'\d STORE $2\_s*' ..
|
||||||
|
|
||||||
'endfor\_s*' ..
|
'endfor\_s*' ..
|
||||||
'\d JUMP -> 5\_s*' ..
|
'\d JUMP -> 5\_s*' ..
|
||||||
'8 DROP\_s*' ..
|
'8 DROP\_s*' ..
|
||||||
@@ -2499,7 +2520,7 @@ def Test_silent_while()
|
|||||||
'\d LOADG g:not\_s*' ..
|
'\d LOADG g:not\_s*' ..
|
||||||
'\d COND2BOOL\_s*' ..
|
'\d COND2BOOL\_s*' ..
|
||||||
'\d CMDMOD_REV\_s*' ..
|
'\d CMDMOD_REV\_s*' ..
|
||||||
'\d JUMP_IF_FALSE -> 6\_s*' ..
|
'\d WHILE $0 -> 6\_s*' ..
|
||||||
|
|
||||||
'endwhile\_s*' ..
|
'endwhile\_s*' ..
|
||||||
'\d JUMP -> 0\_s*' ..
|
'\d JUMP -> 0\_s*' ..
|
||||||
@@ -2691,17 +2712,17 @@ def Test_debug_for()
|
|||||||
'4 STORE -1 in $0\_s*' ..
|
'4 STORE -1 in $0\_s*' ..
|
||||||
'5 PUSHNR 0\_s*' ..
|
'5 PUSHNR 0\_s*' ..
|
||||||
'6 NEWLIST size 1\_s*' ..
|
'6 NEWLIST size 1\_s*' ..
|
||||||
'7 DEBUG line 2-2 varcount 2\_s*' ..
|
'7 DEBUG line 2-2 varcount 3\_s*' ..
|
||||||
'8 FOR $0 -> 15\_s*' ..
|
'8 FOR $0 -> 15\_s*' ..
|
||||||
'9 STORE $1\_s*' ..
|
'9 STORE $2\_s*' ..
|
||||||
|
|
||||||
'echo a\_s*' ..
|
'echo a\_s*' ..
|
||||||
'10 DEBUG line 3-3 varcount 2\_s*' ..
|
'10 DEBUG line 3-3 varcount 3\_s*' ..
|
||||||
'11 LOAD $1\_s*' ..
|
'11 LOAD $2\_s*' ..
|
||||||
'12 ECHO 1\_s*' ..
|
'12 ECHO 1\_s*' ..
|
||||||
|
|
||||||
'endfor\_s*' ..
|
'endfor\_s*' ..
|
||||||
'13 DEBUG line 4-4 varcount 2\_s*' ..
|
'13 DEBUG line 4-4 varcount 3\_s*' ..
|
||||||
'14 JUMP -> 7\_s*' ..
|
'14 JUMP -> 7\_s*' ..
|
||||||
'15 DROP\_s*' ..
|
'15 DROP\_s*' ..
|
||||||
'16 RETURN void*',
|
'16 RETURN void*',
|
||||||
|
@@ -703,6 +703,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
470,
|
||||||
/**/
|
/**/
|
||||||
469,
|
469,
|
||||||
/**/
|
/**/
|
||||||
|
31
src/vim9.h
31
src/vim9.h
@@ -122,6 +122,9 @@ typedef enum {
|
|||||||
|
|
||||||
// loop
|
// loop
|
||||||
ISN_FOR, // get next item from a list, uses isn_arg.forloop
|
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_TRY, // add entry to ec_trystack, uses isn_arg.tryref
|
||||||
ISN_THROW, // pop value of stack, store in v:exception
|
ISN_THROW, // pop value of stack, store in v:exception
|
||||||
@@ -240,6 +243,7 @@ typedef enum {
|
|||||||
JUMP_ALWAYS,
|
JUMP_ALWAYS,
|
||||||
JUMP_NEVER,
|
JUMP_NEVER,
|
||||||
JUMP_IF_FALSE, // pop and jump if false
|
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_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_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
|
JUMP_IF_COND_FALSE, // jump if top of stack is false, drop if not
|
||||||
@@ -263,6 +267,19 @@ typedef struct {
|
|||||||
int for_end; // position to jump to after done
|
int for_end; // position to jump to after done
|
||||||
} forloop_T;
|
} 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
|
// indirect arguments to ISN_TRY
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int try_catch; // position to jump to on throw
|
int try_catch; // position to jump to on throw
|
||||||
@@ -446,6 +463,8 @@ struct isn_S {
|
|||||||
jump_T jump;
|
jump_T jump;
|
||||||
jumparg_T jumparg;
|
jumparg_T jumparg;
|
||||||
forloop_T forloop;
|
forloop_T forloop;
|
||||||
|
whileloop_T whileloop;
|
||||||
|
endloop_T endloop;
|
||||||
try_T tryref;
|
try_T tryref;
|
||||||
trycont_T trycont;
|
trycont_T trycont;
|
||||||
cbfunc_T bfunc;
|
cbfunc_T bfunc;
|
||||||
@@ -597,6 +616,9 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int ws_top_label; // instruction idx at WHILE
|
int ws_top_label; // instruction idx at WHILE
|
||||||
endlabel_T *ws_end_label; // instructions to set end
|
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;
|
} whilescope_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -605,6 +627,9 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
int fs_top_label; // instruction idx at FOR
|
int fs_top_label; // instruction idx at FOR
|
||||||
endlabel_T *fs_end_label; // break instructions
|
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;
|
} forscope_T;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -726,8 +751,10 @@ struct cctx_S {
|
|||||||
|
|
||||||
garray_T ctx_locals; // currently visible local variables
|
garray_T ctx_locals; // currently visible local variables
|
||||||
|
|
||||||
int ctx_has_closure; // set to one if a closure was created in
|
int ctx_has_closure; // set to one if a FUNCREF was used in the
|
||||||
// the function
|
// function
|
||||||
|
int ctx_closure_count; // incremented for each closure created in
|
||||||
|
// the function.
|
||||||
|
|
||||||
skip_T ctx_skip;
|
skip_T ctx_skip;
|
||||||
scope_T *ctx_scope; // current scope, NULL at toplevel
|
scope_T *ctx_scope; // current scope, NULL at toplevel
|
||||||
|
110
src/vim9cmds.c
110
src/vim9cmds.c
@@ -278,10 +278,15 @@ compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* generate a jump to the ":endif"/":endfor"/":endwhile"/":finally"/":endtry".
|
* Generate a jump to the ":endif"/":endfor"/":endwhile"/":finally"/":endtry".
|
||||||
|
* "funcref_idx" is used for JUMP_WHILE_FALSE
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
compile_jump_to_end(endlabel_T **el, jumpwhen_T when, cctx_T *cctx)
|
compile_jump_to_end(
|
||||||
|
endlabel_T **el,
|
||||||
|
jumpwhen_T when,
|
||||||
|
int funcref_idx,
|
||||||
|
cctx_T *cctx)
|
||||||
{
|
{
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
endlabel_T *endlabel = ALLOC_CLEAR_ONE(endlabel_T);
|
endlabel_T *endlabel = ALLOC_CLEAR_ONE(endlabel_T);
|
||||||
@@ -292,6 +297,9 @@ compile_jump_to_end(endlabel_T **el, jumpwhen_T when, cctx_T *cctx)
|
|||||||
*el = endlabel;
|
*el = endlabel;
|
||||||
endlabel->el_end_label = instr->ga_len;
|
endlabel->el_end_label = instr->ga_len;
|
||||||
|
|
||||||
|
if (when == JUMP_WHILE_FALSE)
|
||||||
|
generate_WHILE(cctx, funcref_idx);
|
||||||
|
else
|
||||||
generate_JUMP(cctx, when, 0);
|
generate_JUMP(cctx, when, 0);
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
@@ -564,7 +572,7 @@ compile_elseif(char_u *arg, cctx_T *cctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
|
if (compile_jump_to_end(&scope->se_u.se_if.is_end_label,
|
||||||
JUMP_ALWAYS, cctx) == FAIL)
|
JUMP_ALWAYS, 0, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
// previous "if" or "elseif" jumps here
|
// previous "if" or "elseif" jumps here
|
||||||
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
|
isn = ((isn_T *)instr->ga_data) + scope->se_u.se_if.is_if_label;
|
||||||
@@ -695,7 +703,7 @@ compile_else(char_u *arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
if (!cctx->ctx_had_return
|
if (!cctx->ctx_had_return
|
||||||
&& compile_jump_to_end(&scope->se_u.se_if.is_end_label,
|
&& compile_jump_to_end(&scope->se_u.se_if.is_end_label,
|
||||||
JUMP_ALWAYS, cctx) == FAIL)
|
JUMP_ALWAYS, 0, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -771,14 +779,15 @@ compile_endif(char_u *arg, cctx_T *cctx)
|
|||||||
* Compile "for var in expr":
|
* Compile "for var in expr":
|
||||||
*
|
*
|
||||||
* Produces instructions:
|
* Produces instructions:
|
||||||
* PUSHNR -1
|
* STORE -1 in loop-idx Set index to -1
|
||||||
* STORE loop-idx Set index to -1
|
* EVAL expr Result of "expr" on top of stack
|
||||||
* EVAL expr result of "expr" on top of stack
|
|
||||||
* top: FOR loop-idx, end Increment index, use list on bottom of stack
|
* top: FOR loop-idx, end Increment index, use list on bottom of stack
|
||||||
* - if beyond end, jump to "end"
|
* - if beyond end, jump to "end"
|
||||||
* - otherwise get item from list and push it
|
* - otherwise get item from list and push it
|
||||||
|
* - store ec_funcrefs in var "loop-idx" + 1
|
||||||
* STORE var Store item in "var"
|
* STORE var Store item in "var"
|
||||||
* ... body ...
|
* ... body ...
|
||||||
|
* ENDLOOP funcref-idx off count Only if closure uses local var
|
||||||
* JUMP top Jump back to repeat
|
* JUMP top Jump back to repeat
|
||||||
* end: DROP Drop the result of "expr"
|
* end: DROP Drop the result of "expr"
|
||||||
*
|
*
|
||||||
@@ -801,7 +810,9 @@ compile_for(char_u *arg_start, cctx_T *cctx)
|
|||||||
size_t varlen;
|
size_t varlen;
|
||||||
garray_T *instr = &cctx->ctx_instr;
|
garray_T *instr = &cctx->ctx_instr;
|
||||||
scope_T *scope;
|
scope_T *scope;
|
||||||
|
forscope_T *forscope;
|
||||||
lvar_T *loop_lvar; // loop iteration variable
|
lvar_T *loop_lvar; // loop iteration variable
|
||||||
|
lvar_T *funcref_lvar;
|
||||||
lvar_T *var_lvar; // variable for "var"
|
lvar_T *var_lvar; // variable for "var"
|
||||||
type_T *vartype;
|
type_T *vartype;
|
||||||
type_T *item_type = &t_any;
|
type_T *item_type = &t_any;
|
||||||
@@ -845,18 +856,28 @@ compile_for(char_u *arg_start, cctx_T *cctx)
|
|||||||
scope = new_scope(cctx, FOR_SCOPE);
|
scope = new_scope(cctx, FOR_SCOPE);
|
||||||
if (scope == NULL)
|
if (scope == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
forscope = &scope->se_u.se_for;
|
||||||
|
|
||||||
// Reserve a variable to store the loop iteration counter and initialize it
|
// Reserve a variable to store the loop iteration counter and initialize it
|
||||||
// to -1.
|
// to -1.
|
||||||
loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
|
loop_lvar = reserve_local(cctx, (char_u *)"", 0, FALSE, &t_number);
|
||||||
if (loop_lvar == NULL)
|
if (loop_lvar == NULL)
|
||||||
{
|
{
|
||||||
// out of memory
|
|
||||||
drop_scope(cctx);
|
drop_scope(cctx);
|
||||||
return NULL;
|
return NULL; // out of memory
|
||||||
}
|
}
|
||||||
generate_STORENR(cctx, loop_lvar->lv_idx, -1);
|
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"
|
// compile "expr", it remains on the stack until "endfor"
|
||||||
arg = p;
|
arg = p;
|
||||||
if (compile_expr0(&arg, cctx) == FAIL)
|
if (compile_expr0(&arg, cctx) == FAIL)
|
||||||
@@ -901,7 +922,7 @@ compile_for(char_u *arg_start, cctx_T *cctx)
|
|||||||
generate_undo_cmdmods(cctx);
|
generate_undo_cmdmods(cctx);
|
||||||
|
|
||||||
// "for_end" is set when ":endfor" is found
|
// "for_end" is set when ":endfor" is found
|
||||||
scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
|
forscope->fs_top_label = current_instr_idx(cctx);
|
||||||
|
|
||||||
if (cctx->ctx_compile_type == CT_DEBUG)
|
if (cctx->ctx_compile_type == CT_DEBUG)
|
||||||
{
|
{
|
||||||
@@ -1019,6 +1040,11 @@ compile_for(char_u *arg_start, cctx_T *cctx)
|
|||||||
arg = skipwhite(p);
|
arg = skipwhite(p);
|
||||||
vim_free(name);
|
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;
|
return arg_end;
|
||||||
@@ -1029,6 +1055,23 @@ failed:
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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"
|
* compile "endfor"
|
||||||
*/
|
*/
|
||||||
@@ -1052,6 +1095,14 @@ compile_endfor(char_u *arg, cctx_T *cctx)
|
|||||||
cctx->ctx_scope = scope->se_outer;
|
cctx->ctx_scope = scope->se_outer;
|
||||||
if (cctx->ctx_skip != SKIP_YES)
|
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);
|
unwind_locals(cctx, scope->se_local_count);
|
||||||
|
|
||||||
// At end of ":for" scope jump back to the FOR instruction.
|
// At end of ":for" scope jump back to the FOR instruction.
|
||||||
@@ -1081,8 +1132,9 @@ compile_endfor(char_u *arg, cctx_T *cctx)
|
|||||||
*
|
*
|
||||||
* Produces instructions:
|
* Produces instructions:
|
||||||
* top: EVAL expr Push result of "expr"
|
* top: EVAL expr Push result of "expr"
|
||||||
* JUMP_IF_FALSE end jump if false
|
* WHILE funcref-idx end Jump if false
|
||||||
* ... body ...
|
* ... body ...
|
||||||
|
* ENDLOOP funcref-idx off count only if closure uses local var
|
||||||
* JUMP top Jump back to repeat
|
* JUMP top Jump back to repeat
|
||||||
* end:
|
* end:
|
||||||
*
|
*
|
||||||
@@ -1092,13 +1144,29 @@ compile_while(char_u *arg, cctx_T *cctx)
|
|||||||
{
|
{
|
||||||
char_u *p = arg;
|
char_u *p = arg;
|
||||||
scope_T *scope;
|
scope_T *scope;
|
||||||
|
whilescope_T *whilescope;
|
||||||
|
lvar_T *funcref_lvar;
|
||||||
|
|
||||||
scope = new_scope(cctx, WHILE_SCOPE);
|
scope = new_scope(cctx, WHILE_SCOPE);
|
||||||
if (scope == NULL)
|
if (scope == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
whilescope = &scope->se_u.se_while;
|
||||||
|
|
||||||
// "endwhile" jumps back here, one before when profiling or using cmdmods
|
// "endwhile" jumps back here, one before when profiling or using cmdmods
|
||||||
scope->se_u.se_while.ws_top_label = current_instr_idx(cctx);
|
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"
|
// compile "expr"
|
||||||
if (compile_expr0(&p, cctx) == FAIL)
|
if (compile_expr0(&p, cctx) == FAIL)
|
||||||
@@ -1119,8 +1187,8 @@ compile_while(char_u *arg, cctx_T *cctx)
|
|||||||
generate_undo_cmdmods(cctx);
|
generate_undo_cmdmods(cctx);
|
||||||
|
|
||||||
// "while_end" is set when ":endwhile" is found
|
// "while_end" is set when ":endwhile" is found
|
||||||
if (compile_jump_to_end(&scope->se_u.se_while.ws_end_label,
|
if (compile_jump_to_end(&whilescope->ws_end_label,
|
||||||
JUMP_IF_FALSE, cctx) == FAIL)
|
JUMP_WHILE_FALSE, funcref_lvar->lv_idx, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1146,6 +1214,16 @@ compile_endwhile(char_u *arg, cctx_T *cctx)
|
|||||||
cctx->ctx_scope = scope->se_outer;
|
cctx->ctx_scope = scope->se_outer;
|
||||||
if (cctx->ctx_skip != SKIP_YES)
|
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);
|
unwind_locals(cctx, scope->se_local_count);
|
||||||
|
|
||||||
#ifdef FEAT_PROFILE
|
#ifdef FEAT_PROFILE
|
||||||
@@ -1250,7 +1328,7 @@ compile_break(char_u *arg, cctx_T *cctx)
|
|||||||
|
|
||||||
// Jump to the end of the FOR or WHILE loop. The instruction index will be
|
// Jump to the end of the FOR or WHILE loop. The instruction index will be
|
||||||
// filled in later.
|
// filled in later.
|
||||||
if (compile_jump_to_end(el, JUMP_ALWAYS, cctx) == FAIL)
|
if (compile_jump_to_end(el, JUMP_ALWAYS, 0, cctx) == FAIL)
|
||||||
return FAIL;
|
return FAIL;
|
||||||
|
|
||||||
return arg;
|
return arg;
|
||||||
@@ -1397,7 +1475,7 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
|
|||||||
#endif
|
#endif
|
||||||
// Jump from end of previous block to :finally or :endtry
|
// Jump from end of previous block to :finally or :endtry
|
||||||
if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
|
if (compile_jump_to_end(&scope->se_u.se_try.ts_end_label,
|
||||||
JUMP_ALWAYS, cctx) == FAIL)
|
JUMP_ALWAYS, 0, cctx) == FAIL)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
// End :try or :catch scope: set value in ISN_TRY instruction
|
// End :try or :catch scope: set value in ISN_TRY instruction
|
||||||
|
@@ -3449,8 +3449,14 @@ nextline:
|
|||||||
}
|
}
|
||||||
dfunc->df_varcount = dfunc->df_var_names.ga_len;
|
dfunc->df_varcount = dfunc->df_var_names.ga_len;
|
||||||
dfunc->df_has_closure = cctx.ctx_has_closure;
|
dfunc->df_has_closure = cctx.ctx_has_closure;
|
||||||
|
|
||||||
if (cctx.ctx_outer_used)
|
if (cctx.ctx_outer_used)
|
||||||
|
{
|
||||||
ufunc->uf_flags |= FC_CLOSURE;
|
ufunc->uf_flags |= FC_CLOSURE;
|
||||||
|
if (outer_cctx != NULL)
|
||||||
|
++outer_cctx->ctx_closure_count;
|
||||||
|
}
|
||||||
|
|
||||||
ufunc->uf_def_status = UF_COMPILED;
|
ufunc->uf_def_status = UF_COMPILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -504,7 +504,8 @@ call_dfunc(
|
|||||||
// - if needed: a counter for number of closures created in
|
// - if needed: a counter for number of closures created in
|
||||||
// ectx->ec_funcrefs.
|
// ectx->ec_funcrefs.
|
||||||
varcount = dfunc->df_varcount + dfunc->df_has_closure;
|
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;
|
return FAIL;
|
||||||
|
|
||||||
// If depth of calling is getting too high, don't execute the function.
|
// 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);
|
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->v_type = VAR_NUMBER;
|
||||||
tv->vval.v_number = 0;
|
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)
|
dfunc_T *dfunc = ((dfunc_T *)def_functions.ga_data)
|
||||||
+ ectx->ec_dfunc_idx;
|
+ ectx->ec_dfunc_idx;
|
||||||
|
|
||||||
// The closure may need to find arguments and local variables in the
|
// The closure may need to find arguments and local variables of the
|
||||||
// current stack.
|
// current function in the stack.
|
||||||
pt->pt_outer.out_stack = &ectx->ec_stack;
|
pt->pt_outer.out_stack = &ectx->ec_stack;
|
||||||
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
|
pt->pt_outer.out_frame_idx = ectx->ec_frame_idx;
|
||||||
if (ectx->ec_outer_ref != NULL)
|
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
|
// If the function currently executing returns and the closure is still
|
||||||
// need to make a copy of the context (arguments and local variables).
|
// 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.
|
// Store a reference to the partial so we can handle that.
|
||||||
if (GA_GROW_FAILS(&ectx->ec_funcrefs, 1))
|
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)
|
execute_for(isn_T *iptr, ectx_T *ectx)
|
||||||
{
|
{
|
||||||
typval_T *tv;
|
typval_T *tv;
|
||||||
|
int jump = FALSE;
|
||||||
typval_T *ltv = STACK_TV_BOT(-1);
|
typval_T *ltv = STACK_TV_BOT(-1);
|
||||||
typval_T *idxtv =
|
typval_T *idxtv =
|
||||||
STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
|
STACK_TV_VAR(iptr->isn_arg.forloop.for_idx);
|
||||||
@@ -2492,9 +2497,7 @@ execute_for(isn_T *iptr, ectx_T *ectx)
|
|||||||
if (list == NULL
|
if (list == NULL
|
||||||
|| idxtv->vval.v_number >= list->lv_len)
|
|| idxtv->vval.v_number >= list->lv_len)
|
||||||
{
|
{
|
||||||
// past the end of the list, jump to "endfor"
|
jump = TRUE;
|
||||||
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
|
|
||||||
may_restore_cmdmod(&ectx->ec_funclocal);
|
|
||||||
}
|
}
|
||||||
else if (list->lv_first == &range_list_item)
|
else if (list->lv_first == &range_list_item)
|
||||||
{
|
{
|
||||||
@@ -2524,9 +2527,7 @@ execute_for(isn_T *iptr, ectx_T *ectx)
|
|||||||
++idxtv->vval.v_number;
|
++idxtv->vval.v_number;
|
||||||
if (str == NULL || str[idxtv->vval.v_number] == NUL)
|
if (str == NULL || str[idxtv->vval.v_number] == NUL)
|
||||||
{
|
{
|
||||||
// past the end of the string, jump to "endfor"
|
jump = TRUE;
|
||||||
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
|
|
||||||
may_restore_cmdmod(&ectx->ec_funclocal);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2557,12 +2558,9 @@ execute_for(isn_T *iptr, ectx_T *ectx)
|
|||||||
|
|
||||||
// The index is for the previous byte.
|
// The index is for the previous byte.
|
||||||
++idxtv->vval.v_number;
|
++idxtv->vval.v_number;
|
||||||
if (blob == NULL
|
if (blob == NULL || idxtv->vval.v_number >= blob_len(blob))
|
||||||
|| idxtv->vval.v_number >= blob_len(blob))
|
|
||||||
{
|
{
|
||||||
// past the end of the blob, jump to "endfor"
|
jump = TRUE;
|
||||||
ectx->ec_iidx = iptr->isn_arg.forloop.for_end;
|
|
||||||
may_restore_cmdmod(&ectx->ec_funclocal);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2580,6 +2578,33 @@ execute_for(isn_T *iptr, ectx_T *ectx)
|
|||||||
vartype_name(ltv->v_type));
|
vartype_name(ltv->v_type));
|
||||||
return FAIL;
|
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;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3989,6 +4014,31 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
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
|
// Jump if an argument with a default value was already set and not
|
||||||
// v:none.
|
// v:none.
|
||||||
case ISN_JUMP_IF_ARG_SET:
|
case ISN_JUMP_IF_ARG_SET:
|
||||||
@@ -4005,6 +4055,12 @@ exec_instructions(ectx_T *ectx)
|
|||||||
goto theend;
|
goto theend;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// end of a for or while loop
|
||||||
|
case ISN_ENDLOOP:
|
||||||
|
if (execute_endloop(iptr, ectx) == FAIL)
|
||||||
|
goto theend;
|
||||||
|
break;
|
||||||
|
|
||||||
// start of ":try" block
|
// start of ":try" block
|
||||||
case ISN_TRY:
|
case ISN_TRY:
|
||||||
{
|
{
|
||||||
@@ -6185,6 +6241,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case JUMP_IF_FALSE:
|
case JUMP_IF_FALSE:
|
||||||
when = "JUMP_IF_FALSE";
|
when = "JUMP_IF_FALSE";
|
||||||
break;
|
break;
|
||||||
|
case JUMP_WHILE_FALSE:
|
||||||
|
when = "JUMP_WHILE_FALSE"; // unused
|
||||||
|
break;
|
||||||
case JUMP_IF_COND_FALSE:
|
case JUMP_IF_COND_FALSE:
|
||||||
when = "JUMP_IF_COND_FALSE";
|
when = "JUMP_IF_COND_FALSE";
|
||||||
break;
|
break;
|
||||||
@@ -6212,6 +6271,27 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
}
|
}
|
||||||
break;
|
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:
|
case ISN_TRY:
|
||||||
{
|
{
|
||||||
try_T *try = &iptr->isn_arg.tryref;
|
try_T *try = &iptr->isn_arg.tryref;
|
||||||
|
@@ -1283,6 +1283,27 @@ generate_JUMP(cctx_T *cctx, jumpwhen_T when, int where)
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
* Generate an ISN_JUMP_IF_ARG_SET instruction.
|
||||||
*/
|
*/
|
||||||
@@ -1312,6 +1333,25 @@ generate_FOR(cctx_T *cctx, int loop_idx)
|
|||||||
// type doesn't matter, will be stored next
|
// type doesn't matter, will be stored next
|
||||||
return push_type_stack(cctx, &t_any);
|
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.
|
* Generate an ISN_TRYCONT instruction.
|
||||||
*/
|
*/
|
||||||
@@ -2295,6 +2335,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_ECHOERR:
|
case ISN_ECHOERR:
|
||||||
case ISN_ECHOMSG:
|
case ISN_ECHOMSG:
|
||||||
case ISN_ECHOWINDOW:
|
case ISN_ECHOWINDOW:
|
||||||
|
case ISN_ENDLOOP:
|
||||||
case ISN_ENDTRY:
|
case ISN_ENDTRY:
|
||||||
case ISN_EXECCONCAT:
|
case ISN_EXECCONCAT:
|
||||||
case ISN_EXECUTE:
|
case ISN_EXECUTE:
|
||||||
@@ -2341,10 +2382,10 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_RETURN_VOID:
|
case ISN_RETURN_VOID:
|
||||||
case ISN_SHUFFLE:
|
case ISN_SHUFFLE:
|
||||||
case ISN_SLICE:
|
case ISN_SLICE:
|
||||||
|
case ISN_SOURCE:
|
||||||
case ISN_STORE:
|
case ISN_STORE:
|
||||||
case ISN_STOREINDEX:
|
case ISN_STOREINDEX:
|
||||||
case ISN_STORENR:
|
case ISN_STORENR:
|
||||||
case ISN_SOURCE:
|
|
||||||
case ISN_STOREOUTER:
|
case ISN_STOREOUTER:
|
||||||
case ISN_STORERANGE:
|
case ISN_STORERANGE:
|
||||||
case ISN_STOREREG:
|
case ISN_STOREREG:
|
||||||
@@ -2357,6 +2398,7 @@ delete_instr(isn_T *isn)
|
|||||||
case ISN_UNLETRANGE:
|
case ISN_UNLETRANGE:
|
||||||
case ISN_UNPACK:
|
case ISN_UNPACK:
|
||||||
case ISN_USEDICT:
|
case ISN_USEDICT:
|
||||||
|
case ISN_WHILE:
|
||||||
// nothing allocated
|
// nothing allocated
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user