Patch 8.2.3364

6 views
Skip to first unread message

Bram Moolenaar

unread,
Aug 21, 2021, 11:27:32 AM8/21/21
to vim...@googlegroups.com

Patch 8.2.3364
Problem: Vim9: crash when :for is skipped.
Solution: Skip more code generation. (Naruhiko Nishino, closes #8777)
Files: src/vim9compile.c, src/testdir/test_vim9_script.vim


*** ../vim-8.2.3363/src/vim9compile.c 2021-08-20 20:54:20.558119674 +0200
--- src/vim9compile.c 2021-08-21 17:25:19.035271801 +0200
***************
*** 8041,8191 ****
}
arg_end = arg;

! // If we know the type of "var" and it is a not a supported type we can
! // 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)
{
! 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))
{
! 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)
! {
! assign_dest_T dest = dest_local;
! int opt_flags = 0;
! int vimvaridx = -1;
! type_T *type = &t_any;
! type_T *lhs_type = &t_any;
! where_T where = WHERE_INIT;
!
! p = skip_var_one(arg, FALSE);
! varlen = p - arg;
! 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)
! goto failed;
! if (dest != dest_local)
! {
! if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
! 0, 0, type, name) == FAIL)
! 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;
}

! 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 (cctx->ctx_compile_type == CT_DEBUG)
! {
! int save_prev_lnum = cctx->ctx_prev_lnum;

! // Add ISN_DEBUG here, so that the loop variables can be inspected.
! // 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;
--- 8041,8194 ----
}
arg_end = arg;

! if (cctx->ctx_skip != SKIP_YES)
{
! // If we know the type of "var" and it is a not a supported type we can
! // 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)
! {
! 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))
! {
! 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)
{
! assign_dest_T dest = dest_local;
! int opt_flags = 0;
! int vimvaridx = -1;
! type_T *type = &t_any;
! type_T *lhs_type = &t_any;
! where_T where = WHERE_INIT;
!
! p = skip_var_one(arg, FALSE);
! varlen = p - arg;
! 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)
goto failed;
+ if (dest != dest_local)
+ {
+ if (generate_store_var(cctx, dest, opt_flags, vimvaridx,
+ 0, 0, type, name) == FAIL)
+ 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;
! }
!
! 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 (cctx->ctx_compile_type == CT_DEBUG)
! {
! int save_prev_lnum = cctx->ctx_prev_lnum;

! // Add ISN_DEBUG here, so that the loop variables can be inspected.
! // 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;
***************
*** 8217,8237 ****
}
forscope = &scope->se_u.se_for;
cctx->ctx_scope = scope->se_outer;
! unwind_locals(cctx, scope->se_local_count);

! // At end of ":for" scope jump back to the FOR instruction.
! generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);

! // 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_arg.forloop.for_end = instr->ga_len;

! // Fill in the "end" label any BREAK statements
! compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx);

! // Below the ":for" scope drop the "expr" list from the stack.
! if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
! return NULL;

vim_free(scope);

--- 8220,8243 ----
}
forscope = &scope->se_u.se_for;
cctx->ctx_scope = scope->se_outer;
! if (cctx->ctx_skip != SKIP_YES)
! {
! unwind_locals(cctx, scope->se_local_count);

! // At end of ":for" scope jump back to the FOR instruction.
! generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label);

! // 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_arg.forloop.for_end = instr->ga_len;

! // Fill in the "end" label any BREAK statements
! compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx);

! // Below the ":for" scope drop the "expr" list from the stack.
! if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL)
! return NULL;
! }

vim_free(scope);

*** ../vim-8.2.3363/src/testdir/test_vim9_script.vim 2021-08-21 17:13:08.569405922 +0200
--- src/testdir/test_vim9_script.vim 2021-08-21 17:20:24.836285504 +0200
***************
*** 2552,2557 ****
--- 2552,2621 ----
delete('Xvim9for.vim')
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()
var lines =<< trim END
var result = ''
*** ../vim-8.2.3363/src/version.c 2021-08-21 17:13:08.569405922 +0200
--- src/version.c 2021-08-21 17:22:19.299865867 +0200
***************
*** 757,758 ****
--- 757,760 ----
{ /* Add new patch number below this line */
+ /**/
+ 3364,
/**/

--
No children may attend school with their breath smelling of "wild onions."
[real standing law in West Virginia, United States of America]

/// 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 ///
Reply all
Reply to author
Forward
0 new messages