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:
@@ -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 = ''
|
||||||
|
@@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -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);
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user