1
0
forked from aniani/vim

patch 8.2.3364: Vim9: crash when :for is skipped

Problem:    Vim9: crash when :for is skipped.
Solution:   Skip more code generation. (Naruhiko Nishino, closes #8777)
This commit is contained in:
rbtnn
2021-08-21 17:26:50 +02:00
committed by Bram Moolenaar
parent b8bd2e6eba
commit bebf06954e
3 changed files with 210 additions and 138 deletions

View File

@@ -2552,6 +2552,70 @@ def Test_for_outside_of_function()
delete('Xvim9for.vim') delete('Xvim9for.vim')
enddef enddef
def Test_for_skipped_block()
# test skipped blocks at outside of function
var lines =<< trim END
var result = []
if true
for n in [1, 2]
result += [n]
endfor
else
for n in [3, 4]
result += [n]
endfor
endif
assert_equal([1, 2], result)
result = []
if false
for n in [1, 2]
result += [n]
endfor
else
for n in [3, 4]
result += [n]
endfor
endif
assert_equal([3, 4], result)
END
CheckDefAndScriptSuccess(lines)
# test skipped blocks at inside of function
lines =<< trim END
def DefTrue()
var result = []
if true
for n in [1, 2]
result += [n]
endfor
else
for n in [3, 4]
result += [n]
endfor
endif
assert_equal([1, 2], result)
enddef
DefTrue()
def DefFalse()
var result = []
if false
for n in [1, 2]
result += [n]
endfor
else
for n in [3, 4]
result += [n]
endfor
endif
assert_equal([3, 4], result)
enddef
DefFalse()
END
CheckDefAndScriptSuccess(lines)
enddef
def Test_for_loop() def Test_for_loop()
var lines =<< trim END var lines =<< trim END
var result = '' var result = ''

View File

@@ -755,6 +755,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 */
/**/
3364,
/**/ /**/
3363, 3363,
/**/ /**/

View File

@@ -8041,151 +8041,154 @@ compile_for(char_u *arg_start, cctx_T *cctx)
} }
arg_end = arg; arg_end = arg;
// If we know the type of "var" and it is a not a supported type we can if (cctx->ctx_skip != SKIP_YES)
// give an error now. {
vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; // If we know the type of "var" and it is a not a supported type we can
if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING // give an error now.
vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1];
if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING
&& vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY)
{
semsg(_(e_for_loop_on_str_not_supported),
vartype_name(vartype->tt_type));
drop_scope(cctx);
return NULL;
}
if (vartype->tt_type == VAR_STRING)
item_type = &t_string;
else if (vartype->tt_type == VAR_BLOB)
item_type = &t_number;
else if (vartype->tt_type == VAR_LIST
&& vartype->tt_member->tt_type != VAR_ANY)
{
if (!var_list)
item_type = vartype->tt_member;
else if (vartype->tt_member->tt_type == VAR_LIST
&& vartype->tt_member->tt_member->tt_type != VAR_ANY)
// TODO: should get the type for each lhs
item_type = vartype->tt_member->tt_member;
}
// CMDMOD_REV must come before the FOR instruction.
generate_undo_cmdmods(cctx);
// "for_end" is set when ":endfor" is found
scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
generate_FOR(cctx, loop_lvar->lv_idx);
arg = arg_start;
if (var_list)
{
generate_UNPACK(cctx, var_count, semicolon);
arg = skipwhite(arg + 1); // skip white after '['
// the list item is replaced by a number of items
if (GA_GROW_FAILS(stack, var_count - 1))
{ {
semsg(_(e_for_loop_on_str_not_supported),
vartype_name(vartype->tt_type));
drop_scope(cctx); drop_scope(cctx);
return NULL; return NULL;
} }
--stack->ga_len;
if (vartype->tt_type == VAR_STRING)
item_type = &t_string;
else if (vartype->tt_type == VAR_BLOB)
item_type = &t_number;
else if (vartype->tt_type == VAR_LIST
&& vartype->tt_member->tt_type != VAR_ANY)
{
if (!var_list)
item_type = vartype->tt_member;
else if (vartype->tt_member->tt_type == VAR_LIST
&& vartype->tt_member->tt_member->tt_type != VAR_ANY)
// TODO: should get the type for each lhs
item_type = vartype->tt_member->tt_member;
}
// CMDMOD_REV must come before the FOR instruction.
generate_undo_cmdmods(cctx);
// "for_end" is set when ":endfor" is found
scope->se_u.se_for.fs_top_label = current_instr_idx(cctx);
generate_FOR(cctx, loop_lvar->lv_idx);
arg = arg_start;
if (var_list)
{
generate_UNPACK(cctx, var_count, semicolon);
arg = skipwhite(arg + 1); // skip white after '['
// the list item is replaced by a number of items
if (GA_GROW_FAILS(stack, var_count - 1))
{
drop_scope(cctx);
return NULL;
}
--stack->ga_len;
for (idx = 0; idx < var_count; ++idx)
{
((type_T **)stack->ga_data)[stack->ga_len] =
(semicolon && idx == 0) ? vartype : item_type;
++stack->ga_len;
}
}
for (idx = 0; idx < var_count; ++idx) for (idx = 0; idx < var_count; ++idx)
{ {
((type_T **)stack->ga_data)[stack->ga_len] = assign_dest_T dest = dest_local;
(semicolon && idx == 0) ? vartype : item_type; int opt_flags = 0;
++stack->ga_len; int vimvaridx = -1;
} type_T *type = &t_any;
} type_T *lhs_type = &t_any;
where_T where = WHERE_INIT;
for (idx = 0; idx < var_count; ++idx) p = skip_var_one(arg, FALSE);
{ varlen = p - arg;
assign_dest_T dest = dest_local; name = vim_strnsave(arg, varlen);
int opt_flags = 0; if (name == NULL)
int vimvaridx = -1; goto failed;
type_T *type = &t_any; if (*p == ':')
type_T *lhs_type = &t_any; {
where_T where = WHERE_INIT; p = skipwhite(p + 1);
lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
}
p = skip_var_one(arg, FALSE); // TODO: script var not supported?
varlen = p - arg; if (get_var_dest(name, &dest, CMD_for, &opt_flags,
name = vim_strnsave(arg, varlen);
if (name == NULL)
goto failed;
if (*p == ':')
{
p = skipwhite(p + 1);
lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE);
}
// TODO: script var not supported?
if (get_var_dest(name, &dest, CMD_for, &opt_flags,
&vimvaridx, &type, cctx) == FAIL) &vimvaridx, &type, cctx) == FAIL)
goto failed; goto failed;
if (dest != dest_local) if (dest != dest_local)
{ {
if (generate_store_var(cctx, dest, opt_flags, vimvaridx, if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
0, 0, type, name) == FAIL) 0, 0, type, name) == FAIL)
goto failed; goto failed;
}
else if (varlen == 1 && *arg == '_')
{
// Assigning to "_": drop the value.
if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
goto failed;
}
else
{
if (lookup_local(arg, varlen, NULL, cctx) == OK)
{
semsg(_(e_variable_already_declared), arg);
goto failed;
} }
else if (varlen == 1 && *arg == '_')
if (STRNCMP(name, "s:", 2) == 0)
{ {
semsg(_(e_cannot_declare_script_variable_in_function), name); // Assigning to "_": drop the value.
goto failed; if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
goto failed;
} }
// Reserve a variable to store "var".
where.wt_index = var_list ? idx + 1 : 0;
where.wt_variable = TRUE;
if (lhs_type == &t_any)
lhs_type = item_type;
else if (item_type != &t_unknown
&& (item_type == &t_any
? need_type(item_type, lhs_type,
-1, 0, cctx, FALSE, FALSE)
: check_type(lhs_type, item_type, TRUE, where))
== FAIL)
goto failed;
var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type);
if (var_lvar == NULL)
// out of memory or used as an argument
goto failed;
if (semicolon && idx == var_count - 1)
var_lvar->lv_type = vartype;
else else
var_lvar->lv_type = item_type; {
generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); if (lookup_local(arg, varlen, NULL, cctx) == OK)
{
semsg(_(e_variable_already_declared), arg);
goto failed;
}
if (STRNCMP(name, "s:", 2) == 0)
{
semsg(_(e_cannot_declare_script_variable_in_function), name);
goto failed;
}
// Reserve a variable to store "var".
where.wt_index = var_list ? idx + 1 : 0;
where.wt_variable = TRUE;
if (lhs_type == &t_any)
lhs_type = item_type;
else if (item_type != &t_unknown
&& (item_type == &t_any
? need_type(item_type, lhs_type,
-1, 0, cctx, FALSE, FALSE)
: check_type(lhs_type, item_type, TRUE, where))
== FAIL)
goto failed;
var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type);
if (var_lvar == NULL)
// out of memory or used as an argument
goto failed;
if (semicolon && idx == var_count - 1)
var_lvar->lv_type = vartype;
else
var_lvar->lv_type = item_type;
generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL);
}
if (*p == ',' || *p == ';')
++p;
arg = skipwhite(p);
vim_free(name);
} }
if (*p == ',' || *p == ';') if (cctx->ctx_compile_type == CT_DEBUG)
++p; {
arg = skipwhite(p); int save_prev_lnum = cctx->ctx_prev_lnum;
vim_free(name);
}
if (cctx->ctx_compile_type == CT_DEBUG) // Add ISN_DEBUG here, so that the loop variables can be inspected.
{ // Use the prev_lnum from the ISN_DEBUG instruction removed above.
int save_prev_lnum = cctx->ctx_prev_lnum; cctx->ctx_prev_lnum = prev_lnum;
generate_instr_debug(cctx);
// Add ISN_DEBUG here, so that the loop variables can be inspected. cctx->ctx_prev_lnum = save_prev_lnum;
// Use the prev_lnum from the ISN_DEBUG instruction removed above. }
cctx->ctx_prev_lnum = prev_lnum;
generate_instr_debug(cctx);
cctx->ctx_prev_lnum = save_prev_lnum;
} }
return arg_end; return arg_end;
@@ -8217,21 +8220,24 @@ compile_endfor(char_u *arg, cctx_T *cctx)
} }
forscope = &scope->se_u.se_for; forscope = &scope->se_u.se_for;
cctx->ctx_scope = scope->se_outer; cctx->ctx_scope = scope->se_outer;
unwind_locals(cctx, scope->se_local_count); if (cctx->ctx_skip != SKIP_YES)
{
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.
generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);
// Fill in the "end" label in the FOR statement so it can jump here. // Fill in the "end" label in the FOR statement so it can jump here.
isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label;
isn->isn_arg.forloop.for_end = instr->ga_len; isn->isn_arg.forloop.for_end = instr->ga_len;
// Fill in the "end" label any BREAK statements // Fill in the "end" label any BREAK statements
compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx); compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx);
// Below the ":for" scope drop the "expr" list from the stack. // Below the ":for" scope drop the "expr" list from the stack.
if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
return NULL; return NULL;
}
vim_free(scope); vim_free(scope);