Mercurial > vim
comparison src/vim9compile.c @ 25656:fe7f45e2895e v8.2.3364
patch 8.2.3364: Vim9: crash when :for is skipped
Commit: https://github.com/vim/vim/commit/bebf06954e1c801870b57e06ab03151c2654d079
Author: rbtnn <naru123456789@gmail.com>
Date: Sat Aug 21 17:26:50 2021 +0200
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 https://github.com/vim/vim/issues/8777)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sat, 21 Aug 2021 17:30:03 +0200 |
parents | 6ed39aa92cb9 |
children | 483b40e87ca5 |
comparison
equal
deleted
inserted
replaced
25655:cf7cd75766d9 | 25656:fe7f45e2895e |
---|---|
8039 drop_scope(cctx); | 8039 drop_scope(cctx); |
8040 return NULL; | 8040 return NULL; |
8041 } | 8041 } |
8042 arg_end = arg; | 8042 arg_end = arg; |
8043 | 8043 |
8044 // If we know the type of "var" and it is a not a supported type we can | 8044 if (cctx->ctx_skip != SKIP_YES) |
8045 // give an error now. | 8045 { |
8046 vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; | 8046 // If we know the type of "var" and it is a not a supported type we can |
8047 if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING | 8047 // give an error now. |
8048 vartype = ((type_T **)stack->ga_data)[stack->ga_len - 1]; | |
8049 if (vartype->tt_type != VAR_LIST && vartype->tt_type != VAR_STRING | |
8048 && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) | 8050 && vartype->tt_type != VAR_BLOB && vartype->tt_type != VAR_ANY) |
8049 { | 8051 { |
8050 semsg(_(e_for_loop_on_str_not_supported), | 8052 semsg(_(e_for_loop_on_str_not_supported), |
8051 vartype_name(vartype->tt_type)); | 8053 vartype_name(vartype->tt_type)); |
8052 drop_scope(cctx); | |
8053 return NULL; | |
8054 } | |
8055 | |
8056 if (vartype->tt_type == VAR_STRING) | |
8057 item_type = &t_string; | |
8058 else if (vartype->tt_type == VAR_BLOB) | |
8059 item_type = &t_number; | |
8060 else if (vartype->tt_type == VAR_LIST | |
8061 && vartype->tt_member->tt_type != VAR_ANY) | |
8062 { | |
8063 if (!var_list) | |
8064 item_type = vartype->tt_member; | |
8065 else if (vartype->tt_member->tt_type == VAR_LIST | |
8066 && vartype->tt_member->tt_member->tt_type != VAR_ANY) | |
8067 // TODO: should get the type for each lhs | |
8068 item_type = vartype->tt_member->tt_member; | |
8069 } | |
8070 | |
8071 // CMDMOD_REV must come before the FOR instruction. | |
8072 generate_undo_cmdmods(cctx); | |
8073 | |
8074 // "for_end" is set when ":endfor" is found | |
8075 scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); | |
8076 | |
8077 generate_FOR(cctx, loop_lvar->lv_idx); | |
8078 | |
8079 arg = arg_start; | |
8080 if (var_list) | |
8081 { | |
8082 generate_UNPACK(cctx, var_count, semicolon); | |
8083 arg = skipwhite(arg + 1); // skip white after '[' | |
8084 | |
8085 // the list item is replaced by a number of items | |
8086 if (GA_GROW_FAILS(stack, var_count - 1)) | |
8087 { | |
8088 drop_scope(cctx); | 8054 drop_scope(cctx); |
8089 return NULL; | 8055 return NULL; |
8090 } | 8056 } |
8091 --stack->ga_len; | 8057 |
8058 if (vartype->tt_type == VAR_STRING) | |
8059 item_type = &t_string; | |
8060 else if (vartype->tt_type == VAR_BLOB) | |
8061 item_type = &t_number; | |
8062 else if (vartype->tt_type == VAR_LIST | |
8063 && vartype->tt_member->tt_type != VAR_ANY) | |
8064 { | |
8065 if (!var_list) | |
8066 item_type = vartype->tt_member; | |
8067 else if (vartype->tt_member->tt_type == VAR_LIST | |
8068 && vartype->tt_member->tt_member->tt_type != VAR_ANY) | |
8069 // TODO: should get the type for each lhs | |
8070 item_type = vartype->tt_member->tt_member; | |
8071 } | |
8072 | |
8073 // CMDMOD_REV must come before the FOR instruction. | |
8074 generate_undo_cmdmods(cctx); | |
8075 | |
8076 // "for_end" is set when ":endfor" is found | |
8077 scope->se_u.se_for.fs_top_label = current_instr_idx(cctx); | |
8078 | |
8079 generate_FOR(cctx, loop_lvar->lv_idx); | |
8080 | |
8081 arg = arg_start; | |
8082 if (var_list) | |
8083 { | |
8084 generate_UNPACK(cctx, var_count, semicolon); | |
8085 arg = skipwhite(arg + 1); // skip white after '[' | |
8086 | |
8087 // the list item is replaced by a number of items | |
8088 if (GA_GROW_FAILS(stack, var_count - 1)) | |
8089 { | |
8090 drop_scope(cctx); | |
8091 return NULL; | |
8092 } | |
8093 --stack->ga_len; | |
8094 for (idx = 0; idx < var_count; ++idx) | |
8095 { | |
8096 ((type_T **)stack->ga_data)[stack->ga_len] = | |
8097 (semicolon && idx == 0) ? vartype : item_type; | |
8098 ++stack->ga_len; | |
8099 } | |
8100 } | |
8101 | |
8092 for (idx = 0; idx < var_count; ++idx) | 8102 for (idx = 0; idx < var_count; ++idx) |
8093 { | 8103 { |
8094 ((type_T **)stack->ga_data)[stack->ga_len] = | 8104 assign_dest_T dest = dest_local; |
8095 (semicolon && idx == 0) ? vartype : item_type; | 8105 int opt_flags = 0; |
8096 ++stack->ga_len; | 8106 int vimvaridx = -1; |
8097 } | 8107 type_T *type = &t_any; |
8098 } | 8108 type_T *lhs_type = &t_any; |
8099 | 8109 where_T where = WHERE_INIT; |
8100 for (idx = 0; idx < var_count; ++idx) | 8110 |
8101 { | 8111 p = skip_var_one(arg, FALSE); |
8102 assign_dest_T dest = dest_local; | 8112 varlen = p - arg; |
8103 int opt_flags = 0; | 8113 name = vim_strnsave(arg, varlen); |
8104 int vimvaridx = -1; | 8114 if (name == NULL) |
8105 type_T *type = &t_any; | 8115 goto failed; |
8106 type_T *lhs_type = &t_any; | 8116 if (*p == ':') |
8107 where_T where = WHERE_INIT; | 8117 { |
8108 | 8118 p = skipwhite(p + 1); |
8109 p = skip_var_one(arg, FALSE); | 8119 lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE); |
8110 varlen = p - arg; | 8120 } |
8111 name = vim_strnsave(arg, varlen); | 8121 |
8112 if (name == NULL) | 8122 // TODO: script var not supported? |
8113 goto failed; | 8123 if (get_var_dest(name, &dest, CMD_for, &opt_flags, |
8114 if (*p == ':') | |
8115 { | |
8116 p = skipwhite(p + 1); | |
8117 lhs_type = parse_type(&p, cctx->ctx_type_list, TRUE); | |
8118 } | |
8119 | |
8120 // TODO: script var not supported? | |
8121 if (get_var_dest(name, &dest, CMD_for, &opt_flags, | |
8122 &vimvaridx, &type, cctx) == FAIL) | 8124 &vimvaridx, &type, cctx) == FAIL) |
8123 goto failed; | 8125 goto failed; |
8124 if (dest != dest_local) | 8126 if (dest != dest_local) |
8125 { | 8127 { |
8126 if (generate_store_var(cctx, dest, opt_flags, vimvaridx, | 8128 if (generate_store_var(cctx, dest, opt_flags, vimvaridx, |
8127 0, 0, type, name) == FAIL) | 8129 0, 0, type, name) == FAIL) |
8128 goto failed; | 8130 goto failed; |
8129 } | 8131 } |
8130 else if (varlen == 1 && *arg == '_') | 8132 else if (varlen == 1 && *arg == '_') |
8131 { | 8133 { |
8132 // Assigning to "_": drop the value. | 8134 // Assigning to "_": drop the value. |
8133 if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) | 8135 if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) |
8134 goto failed; | 8136 goto failed; |
8135 } | 8137 } |
8136 else | 8138 else |
8137 { | 8139 { |
8138 if (lookup_local(arg, varlen, NULL, cctx) == OK) | 8140 if (lookup_local(arg, varlen, NULL, cctx) == OK) |
8139 { | 8141 { |
8140 semsg(_(e_variable_already_declared), arg); | 8142 semsg(_(e_variable_already_declared), arg); |
8141 goto failed; | 8143 goto failed; |
8142 } | 8144 } |
8143 | 8145 |
8144 if (STRNCMP(name, "s:", 2) == 0) | 8146 if (STRNCMP(name, "s:", 2) == 0) |
8145 { | 8147 { |
8146 semsg(_(e_cannot_declare_script_variable_in_function), name); | 8148 semsg(_(e_cannot_declare_script_variable_in_function), name); |
8147 goto failed; | 8149 goto failed; |
8148 } | 8150 } |
8149 | 8151 |
8150 // Reserve a variable to store "var". | 8152 // Reserve a variable to store "var". |
8151 where.wt_index = var_list ? idx + 1 : 0; | 8153 where.wt_index = var_list ? idx + 1 : 0; |
8152 where.wt_variable = TRUE; | 8154 where.wt_variable = TRUE; |
8153 if (lhs_type == &t_any) | 8155 if (lhs_type == &t_any) |
8154 lhs_type = item_type; | 8156 lhs_type = item_type; |
8155 else if (item_type != &t_unknown | 8157 else if (item_type != &t_unknown |
8156 && (item_type == &t_any | 8158 && (item_type == &t_any |
8157 ? need_type(item_type, lhs_type, | 8159 ? need_type(item_type, lhs_type, |
8158 -1, 0, cctx, FALSE, FALSE) | 8160 -1, 0, cctx, FALSE, FALSE) |
8159 : check_type(lhs_type, item_type, TRUE, where)) | 8161 : check_type(lhs_type, item_type, TRUE, where)) |
8160 == FAIL) | 8162 == FAIL) |
8161 goto failed; | 8163 goto failed; |
8162 var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type); | 8164 var_lvar = reserve_local(cctx, arg, varlen, TRUE, lhs_type); |
8163 if (var_lvar == NULL) | 8165 if (var_lvar == NULL) |
8164 // out of memory or used as an argument | 8166 // out of memory or used as an argument |
8165 goto failed; | 8167 goto failed; |
8166 | 8168 |
8167 if (semicolon && idx == var_count - 1) | 8169 if (semicolon && idx == var_count - 1) |
8168 var_lvar->lv_type = vartype; | 8170 var_lvar->lv_type = vartype; |
8169 else | 8171 else |
8170 var_lvar->lv_type = item_type; | 8172 var_lvar->lv_type = item_type; |
8171 generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); | 8173 generate_STORE(cctx, ISN_STORE, var_lvar->lv_idx, NULL); |
8172 } | 8174 } |
8173 | 8175 |
8174 if (*p == ',' || *p == ';') | 8176 if (*p == ',' || *p == ';') |
8175 ++p; | 8177 ++p; |
8176 arg = skipwhite(p); | 8178 arg = skipwhite(p); |
8177 vim_free(name); | 8179 vim_free(name); |
8178 } | 8180 } |
8179 | 8181 |
8180 if (cctx->ctx_compile_type == CT_DEBUG) | 8182 if (cctx->ctx_compile_type == CT_DEBUG) |
8181 { | 8183 { |
8182 int save_prev_lnum = cctx->ctx_prev_lnum; | 8184 int save_prev_lnum = cctx->ctx_prev_lnum; |
8183 | 8185 |
8184 // Add ISN_DEBUG here, so that the loop variables can be inspected. | 8186 // Add ISN_DEBUG here, so that the loop variables can be inspected. |
8185 // Use the prev_lnum from the ISN_DEBUG instruction removed above. | 8187 // Use the prev_lnum from the ISN_DEBUG instruction removed above. |
8186 cctx->ctx_prev_lnum = prev_lnum; | 8188 cctx->ctx_prev_lnum = prev_lnum; |
8187 generate_instr_debug(cctx); | 8189 generate_instr_debug(cctx); |
8188 cctx->ctx_prev_lnum = save_prev_lnum; | 8190 cctx->ctx_prev_lnum = save_prev_lnum; |
8191 } | |
8189 } | 8192 } |
8190 | 8193 |
8191 return arg_end; | 8194 return arg_end; |
8192 | 8195 |
8193 failed: | 8196 failed: |
8215 emsg(_(e_for)); | 8218 emsg(_(e_for)); |
8216 return NULL; | 8219 return NULL; |
8217 } | 8220 } |
8218 forscope = &scope->se_u.se_for; | 8221 forscope = &scope->se_u.se_for; |
8219 cctx->ctx_scope = scope->se_outer; | 8222 cctx->ctx_scope = scope->se_outer; |
8220 unwind_locals(cctx, scope->se_local_count); | 8223 if (cctx->ctx_skip != SKIP_YES) |
8221 | 8224 { |
8222 // At end of ":for" scope jump back to the FOR instruction. | 8225 unwind_locals(cctx, scope->se_local_count); |
8223 generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); | 8226 |
8224 | 8227 // At end of ":for" scope jump back to the FOR instruction. |
8225 // Fill in the "end" label in the FOR statement so it can jump here. | 8228 generate_JUMP(cctx, JUMP_ALWAYS, forscope->fs_top_label); |
8226 isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; | 8229 |
8227 isn->isn_arg.forloop.for_end = instr->ga_len; | 8230 // Fill in the "end" label in the FOR statement so it can jump here. |
8228 | 8231 isn = ((isn_T *)instr->ga_data) + forscope->fs_top_label; |
8229 // Fill in the "end" label any BREAK statements | 8232 isn->isn_arg.forloop.for_end = instr->ga_len; |
8230 compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx); | 8233 |
8231 | 8234 // Fill in the "end" label any BREAK statements |
8232 // Below the ":for" scope drop the "expr" list from the stack. | 8235 compile_fill_jump_to_end(&forscope->fs_end_label, instr->ga_len, cctx); |
8233 if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) | 8236 |
8234 return NULL; | 8237 // Below the ":for" scope drop the "expr" list from the stack. |
8238 if (generate_instr_drop(cctx, ISN_DROP, 1) == NULL) | |
8239 return NULL; | |
8240 } | |
8235 | 8241 |
8236 vim_free(scope); | 8242 vim_free(scope); |
8237 | 8243 |
8238 return arg; | 8244 return arg; |
8239 } | 8245 } |