diff options
author | rbtnn <naru123456789@gmail.com> | 2021-08-21 17:26:50 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2021-08-21 17:26:50 +0200 |
commit | bebf06954e1c801870b57e06ab03151c2654d079 (patch) | |
tree | b6b7aedc49e45774e11ac47f73efdda6944a06f6 | |
parent | b8bd2e6ebab03baf2672067067a599df69a278c0 (diff) | |
download | vim-git-bebf06954e1c801870b57e06ab03151c2654d079.tar.gz |
patch 8.2.3364: Vim9: crash when :for is skippedv8.2.3364
Problem: Vim9: crash when :for is skipped.
Solution: Skip more code generation. (Naruhiko Nishino, closes #8777)
-rw-r--r-- | src/testdir/test_vim9_script.vim | 64 | ||||
-rw-r--r-- | src/version.c | 2 | ||||
-rw-r--r-- | src/vim9compile.c | 266 |
3 files changed, 202 insertions, 130 deletions
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim index fd93c3a8b..cdbf914ab 100644 --- a/src/testdir/test_vim9_script.vim +++ b/src/testdir/test_vim9_script.vim @@ -2552,6 +2552,70 @@ def Test_for_outside_of_function() 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 = '' diff --git a/src/version.c b/src/version.c index 25e0d82ff..146eb8a30 100644 --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3364, +/**/ 3363, /**/ 3362, diff --git a/src/vim9compile.c b/src/vim9compile.c index ccfb4ee16..ee63b32f1 100644 --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -8041,151 +8041,154 @@ compile_for(char_u *arg_start, cctx_T *cctx) } 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) + if (cctx->ctx_skip != SKIP_YES) { - semsg(_(e_for_loop_on_str_not_supported), + // 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; - } + 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 + 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; - } + { + 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); + // 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); + // "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); + 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)) + arg = arg_start; + if (var_list) { - drop_scope(cctx); - return NULL; + 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; + } } - --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; - } - } + 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; - 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); - } + 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, + // 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) + if (dest != dest_local) { - semsg(_(e_variable_already_declared), arg); - goto failed; + if (generate_store_var(cctx, dest, opt_flags, vimvaridx, + 0, 0, type, name) == FAIL) + goto failed; } - - if (STRNCMP(name, "s:", 2) == 0) + else if (varlen == 1 && *arg == '_') { - semsg(_(e_cannot_declare_script_variable_in_function), name); - goto failed; + // 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; + } - // 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, + 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; + : 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 (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 == ';') - ++p; - arg = skipwhite(p); - vim_free(name); - } - - if (cctx->ctx_compile_type == CT_DEBUG) - { - int save_prev_lnum = cctx->ctx_prev_lnum; + 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; + // 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,21 +8220,24 @@ compile_endfor(char_u *arg, cctx_T *cctx) } forscope = &scope->se_u.se_for; 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. - generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); + // 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 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); + // 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; + // 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); |