Merge pull request #3722 from ZyX-I/fix-3721

shada: Continue dumping when variables failed to dump
This commit is contained in:
Justin M. Keyes 2015-12-18 11:55:15 -05:00
commit e123675bcc
3 changed files with 107 additions and 51 deletions

View File

@ -122,7 +122,7 @@ KHASH_SET_INIT_STR(strset)
// E576: Missing '>' // E576: Missing '>'
// E577: Illegal register name // E577: Illegal register name
// E886: Can't rename viminfo file to %s! // E886: Can't rename viminfo file to %s!
// Now only five of them are used: // Now only six of them are used:
// E137: ShaDa file is not writeable (for pre-open checks) // E137: ShaDa file is not writeable (for pre-open checks)
// E138: All %s.tmp.X files exist, cannot write ShaDa file! // E138: All %s.tmp.X files exist, cannot write ShaDa file!
// RCERR (E576) for critical read errors. // RCERR (E576) for critical read errors.
@ -130,6 +130,7 @@ KHASH_SET_INIT_STR(strset)
// RERR (E575) for various errors inside read ShaDa file. // RERR (E575) for various errors inside read ShaDa file.
// SERR (E886) for various “system” errors (always contains output of // SERR (E886) for various “system” errors (always contains output of
// strerror) // strerror)
// WERR (E574) for various ignorable write errors
/// Common prefix for all errors inside ShaDa file /// Common prefix for all errors inside ShaDa file
/// ///
@ -148,6 +149,9 @@ KHASH_SET_INIT_STR(strset)
/// Common prefix for all “rename” errors /// Common prefix for all “rename” errors
#define RNERR "E136: " #define RNERR "E136: "
/// Common prefix for all ignorable “write” errors
#define WERR "E574: "
/// Flags for shada_read_file and children /// Flags for shada_read_file and children
typedef enum { typedef enum {
kShaDaWantInfo = 1, ///< Load non-mark information kShaDaWantInfo = 1, ///< Load non-mark information
@ -198,6 +202,9 @@ typedef enum {
///< a ShaDa file. ///< a ShaDa file.
kSDWriteFailed, ///< Writing was not successfull (e.g. because there kSDWriteFailed, ///< Writing was not successfull (e.g. because there
///< was no space left on device). ///< was no space left on device).
kSDWriteIgnError, ///< Writing resulted in a error which can be ignored
///< (e.g. when trying to dump a function reference or
///< self-referencing container in a variable).
} ShaDaWriteResult; } ShaDaWriteResult;
/// Flags for shada_read_next_item /// Flags for shada_read_next_item
@ -1666,11 +1673,14 @@ static char *shada_filename(const char *file)
/// @param[in] entry Entry written. /// @param[in] entry Entry written.
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions. /// restrictions.
static bool shada_pack_entry(msgpack_packer *const packer, ///
ShadaEntry entry, /// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError.
const size_t max_kbyte) static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer,
ShadaEntry entry,
const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
ShaDaWriteResult ret = kSDWriteFailed;
msgpack_sbuffer sbuf; msgpack_sbuffer sbuf;
msgpack_sbuffer_init(&sbuf); msgpack_sbuffer_init(&sbuf);
msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write); msgpack_packer *spacker = msgpack_packer_new(&sbuf, &msgpack_sbuffer_write);
@ -1742,6 +1752,9 @@ static bool shada_pack_entry(msgpack_packer *const packer,
msgpack_pack_array(spacker, arr_size); msgpack_pack_array(spacker, arr_size);
PACK_BIN(cstr_as_string(entry.data.global_var.name)); PACK_BIN(cstr_as_string(entry.data.global_var.name));
if (vim_to_msgpack(spacker, &entry.data.global_var.value) == FAIL) { if (vim_to_msgpack(spacker, &entry.data.global_var.value) == FAIL) {
ret = kSDWriteIgnError;
EMSG2(_(WERR "Failed to write variable %s"),
entry.data.global_var.name);
goto shada_pack_entry_error; goto shada_pack_entry_error;
} }
DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements); DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements);
@ -1946,11 +1959,11 @@ static bool shada_pack_entry(msgpack_packer *const packer,
} }
msgpack_packer_free(spacker); msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf); msgpack_sbuffer_destroy(&sbuf);
return true; return kSDWriteSuccessfull;
shada_pack_entry_error: shada_pack_entry_error:
msgpack_packer_free(spacker); msgpack_packer_free(spacker);
msgpack_sbuffer_destroy(&sbuf); msgpack_sbuffer_destroy(&sbuf);
return false; return ret;
} }
#undef PACK_STRING #undef PACK_STRING
@ -1965,13 +1978,13 @@ shada_pack_entry_error:
/// is assumed that entry was already converted. /// is assumed that entry was already converted.
/// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no
/// restrictions. /// restrictions.
static bool shada_pack_encoded_entry(msgpack_packer *const packer, static ShaDaWriteResult shada_pack_encoded_entry(msgpack_packer *const packer,
const vimconv_T *const sd_conv, const vimconv_T *const sd_conv,
PossiblyFreedShadaEntry entry, PossiblyFreedShadaEntry entry,
const size_t max_kbyte) const size_t max_kbyte)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
bool ret = true; ShaDaWriteResult ret = kSDWriteSuccessfull;
if (entry.can_free_entry) { if (entry.can_free_entry) {
ret = shada_pack_entry(packer, entry.data, max_kbyte); ret = shada_pack_entry(packer, entry.data, max_kbyte);
shada_free_shada_entry(&entry.data); shada_free_shada_entry(&entry.data);
@ -2244,9 +2257,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
assert(false); assert(false);
} }
case kSDItemUnknown: { case kSDItemUnknown: {
if (!shada_pack_entry(packer, entry, 0)) { ret = shada_pack_entry(packer, entry, 0);
ret = kSDWriteFailed;
}
shada_free_shada_entry(&entry); shada_free_shada_entry(&entry);
break; break;
} }
@ -2262,9 +2273,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
} }
case kSDItemHistoryEntry: { case kSDItemHistoryEntry: {
if (entry.data.history_item.histtype >= HIST_COUNT) { if (entry.data.history_item.histtype >= HIST_COUNT) {
if (!shada_pack_entry(packer, entry, 0)) { ret = shada_pack_entry(packer, entry, 0);
ret = kSDWriteFailed;
}
shada_free_shada_entry(&entry); shada_free_shada_entry(&entry);
break; break;
} }
@ -2275,9 +2284,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
case kSDItemRegister: { case kSDItemRegister: {
const int idx = op_reg_index(entry.data.reg.name); const int idx = op_reg_index(entry.data.reg.name);
if (idx < 0) { if (idx < 0) {
if (!shada_pack_entry(packer, entry, 0)) { ret = shada_pack_entry(packer, entry, 0);
ret = kSDWriteFailed;
}
shada_free_shada_entry(&entry); shada_free_shada_entry(&entry);
break; break;
} }
@ -2286,9 +2293,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
} }
case kSDItemVariable: { case kSDItemVariable: {
if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) { if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) {
if (!shada_pack_entry(packer, entry, 0)) { ret = shada_pack_entry(packer, entry, 0);
ret = kSDWriteFailed;
}
} }
shada_free_shada_entry(&entry); shada_free_shada_entry(&entry);
break; break;
@ -2296,9 +2301,7 @@ static inline ShaDaWriteResult shada_read_when_writing(
case kSDItemGlobalMark: { case kSDItemGlobalMark: {
const int idx = mark_global_index(entry.data.filemark.name); const int idx = mark_global_index(entry.data.filemark.name);
if (idx < 0) { if (idx < 0) {
if (!shada_pack_entry(packer, entry, 0)) { ret = shada_pack_entry(packer, entry, 0);
ret = kSDWriteFailed;
}
shada_free_shada_entry(&entry); shada_free_shada_entry(&entry);
break; break;
} }
@ -2465,7 +2468,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
} }
// Write header // Write header
if (!shada_pack_entry(packer, (ShadaEntry) { if (shada_pack_entry(packer, (ShadaEntry) {
.type = kSDItemHeader, .type = kSDItemHeader,
.timestamp = os_time(), .timestamp = os_time(),
.data = { .data = {
@ -2486,7 +2489,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}), }),
} }
} }
}, 0)) { }, 0) == kSDWriteFailed) {
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
} }
@ -2526,7 +2529,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
}; };
i++; i++;
} }
if (!shada_pack_entry(packer, buflist_entry, 0)) { if (shada_pack_entry(packer, buflist_entry, 0) == kSDWriteFailed) {
xfree(buflist_entry.data.buffer_list.buffers); xfree(buflist_entry.data.buffer_list.buffers);
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
@ -2552,7 +2555,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
} else { } else {
copy_tv(&vartv, &tgttv); copy_tv(&vartv, &tgttv);
} }
if (!shada_pack_entry(packer, (ShadaEntry) { ShaDaWriteResult spe_ret;
if ((spe_ret = shada_pack_entry(packer, (ShadaEntry) {
.type = kSDItemVariable, .type = kSDItemVariable,
.timestamp = cur_timestamp, .timestamp = cur_timestamp,
.data = { .data = {
@ -2562,7 +2566,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
.additional_elements = NULL, .additional_elements = NULL,
} }
} }
}, max_kbyte)) { }, max_kbyte)) == kSDWriteFailed) {
clear_tv(&vartv); clear_tv(&vartv);
clear_tv(&tgttv); clear_tv(&tgttv);
ret = kSDWriteFailed; ret = kSDWriteFailed;
@ -2570,8 +2574,10 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
} }
clear_tv(&vartv); clear_tv(&vartv);
clear_tv(&tgttv); clear_tv(&tgttv);
int kh_ret; if (spe_ret == kSDWriteSuccessfull) {
(void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); int kh_ret;
(void) kh_put(strset, &wms->dumped_variables, name, &kh_ret);
}
} while (var_iter != NULL); } while (var_iter != NULL);
} }
@ -2828,9 +2834,9 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
do { \ do { \
for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \ for (size_t i_ = 0; i_ < ARRAY_SIZE(wms_array); i_++) { \
if (wms_array[i_].data.type != kSDItemMissing) { \ if (wms_array[i_].data.type != kSDItemMissing) { \
if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \ if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, \
wms_array[i_], \ wms_array[i_], \
max_kbyte)) { \ max_kbyte) == kSDWriteFailed) { \
ret = kSDWriteFailed; \ ret = kSDWriteFailed; \
goto shada_write_exit; \ goto shada_write_exit; \
} \ } \
@ -2840,8 +2846,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
PACK_WMS_ARRAY(wms->global_marks); PACK_WMS_ARRAY(wms->global_marks);
PACK_WMS_ARRAY(wms->registers); PACK_WMS_ARRAY(wms->registers);
for (size_t i = 0; i < wms->jumps_size; i++) { for (size_t i = 0; i < wms->jumps_size; i++) {
if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i], if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms->jumps[i],
max_kbyte)) { max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
} }
@ -2849,8 +2855,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
#define PACK_WMS_ENTRY(wms_entry) \ #define PACK_WMS_ENTRY(wms_entry) \
do { \ do { \
if (wms_entry.data.type != kSDItemMissing) { \ if (wms_entry.data.type != kSDItemMissing) { \
if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \ if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv, wms_entry, \
max_kbyte)) { \ max_kbyte) == kSDWriteFailed) { \
ret = kSDWriteFailed; \ ret = kSDWriteFailed; \
goto shada_write_exit; \ goto shada_write_exit; \
} \ } \
@ -2877,16 +2883,16 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
for (size_t i = 0; i < file_markss_to_dump; i++) { for (size_t i = 0; i < file_markss_to_dump; i++) {
PACK_WMS_ARRAY(all_file_markss[i]->marks); PACK_WMS_ARRAY(all_file_markss[i]->marks);
for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) { for (size_t j = 0; j < all_file_markss[i]->changes_size; j++) {
if (!shada_pack_encoded_entry(packer, &sd_writer->sd_conv, if (shada_pack_encoded_entry(packer, &sd_writer->sd_conv,
all_file_markss[i]->changes[j], all_file_markss[i]->changes[j],
max_kbyte)) { max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
} }
} }
for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) { for (size_t j = 0; j < all_file_markss[i]->additional_marks_size; j++) {
if (!shada_pack_entry(packer, all_file_markss[i]->additional_marks[j], if (shada_pack_entry(packer, all_file_markss[i]->additional_marks[j],
0)) { 0) == kSDWriteFailed) {
shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]); shada_free_shada_entry(&all_file_markss[i]->additional_marks[j]);
ret = kSDWriteFailed; ret = kSDWriteFailed;
goto shada_write_exit; goto shada_write_exit;
@ -2903,16 +2909,15 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
if (dump_one_history[i]) { if (dump_one_history[i]) {
hms_insert_whole_neovim_history(&wms->hms[i]); hms_insert_whole_neovim_history(&wms->hms[i]);
HMS_ITER(&wms->hms[i], cur_entry, { HMS_ITER(&wms->hms[i], cur_entry, {
if (!shada_pack_encoded_entry( if (shada_pack_encoded_entry(
packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) { packer, &sd_writer->sd_conv, (PossiblyFreedShadaEntry) {
.data = cur_entry->data, .data = cur_entry->data,
.can_free_entry = cur_entry->can_free_entry, .can_free_entry = cur_entry->can_free_entry,
}, max_kbyte)) { }, max_kbyte) == kSDWriteFailed) {
ret = kSDWriteFailed; ret = kSDWriteFailed;
break; break;
} }
}) })
hms_dealloc(&wms->hms[i]);
if (ret == kSDWriteFailed) { if (ret == kSDWriteFailed) {
goto shada_write_exit; goto shada_write_exit;
} }
@ -2921,6 +2926,11 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer,
} }
shada_write_exit: shada_write_exit:
for (size_t i = 0; i < HIST_COUNT; i++) {
if (dump_one_history[i]) {
hms_dealloc(&wms->hms[i]);
}
}
kh_dealloc(file_marks, &wms->file_marks); kh_dealloc(file_marks, &wms->file_marks);
kh_dealloc(bufset, &removable_bufs); kh_dealloc(bufset, &removable_bufs);
msgpack_packer_free(packer); msgpack_packer_free(packer);
@ -3043,6 +3053,7 @@ shada_write_file_nomerge: {}
const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge const ShaDaWriteResult sw_ret = shada_write(&sd_writer, (nomerge
? NULL ? NULL
: &sd_reader)); : &sd_reader));
assert(sw_ret != kSDWriteIgnError);
#ifndef UNIX #ifndef UNIX
sd_writer.close(&sd_writer); sd_writer.close(&sd_writer);
#endif #endif

View File

@ -1,6 +1,7 @@
-- ShaDa errors handling support -- ShaDa errors handling support
local helpers = require('test.functional.helpers') local helpers = require('test.functional.helpers')
local nvim_command, eq, exc_exec = helpers.command, helpers.eq, helpers.exc_exec local nvim_command, eq, exc_exec, redir_exec =
helpers.command, helpers.eq, helpers.exc_exec, helpers.redir_exec
local shada_helpers = require('test.functional.shada.helpers') local shada_helpers = require('test.functional.shada.helpers')
local reset, clear, get_shada_rw = local reset, clear, get_shada_rw =
@ -492,4 +493,21 @@ $
eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 47 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname)) eq('Vim(wshada):E576: Error while reading ShaDa file: last entry specified that it occupies 47 bytes, but file ended earlier', exc_exec('wshada ' .. shada_fname))
eq(0, exc_exec('wshada! ' .. shada_fname)) eq(0, exc_exec('wshada! ' .. shada_fname))
end) end)
it('errors when a funcref is stored in a variable', function()
nvim_command('let F = function("tr")')
nvim_command('set shada+=!')
eq('\nE475: Invalid argument: attempt to dump function reference'
.. '\nE574: Failed to write variable F',
redir_exec('wshada'))
end)
it('errors when a self-referencing list is stored in a variable', function()
nvim_command('let L = []')
nvim_command('call add(L, L)')
nvim_command('set shada+=!')
eq('\nE475: Invalid argument: container references itself'
.. '\nE574: Failed to write variable L',
redir_exec('wshada'))
end)
end) end)

View File

@ -1,7 +1,7 @@
-- ShaDa variables saving/reading support -- ShaDa variables saving/reading support
local helpers = require('test.functional.helpers') local helpers = require('test.functional.helpers')
local meths, funcs, nvim_command, eq = local meths, funcs, nvim_command, eq, exc_exec =
helpers.meths, helpers.funcs, helpers.command, helpers.eq helpers.meths, helpers.funcs, helpers.command, helpers.eq, helpers.exc_exec
local shada_helpers = require('test.functional.shada.helpers') local shada_helpers = require('test.functional.shada.helpers')
local reset, set_additional_cmd, clear = local reset, set_additional_cmd, clear =
@ -136,4 +136,31 @@ describe('ShaDa support code', function()
eq({['\171']={{'\171'}, {['\171']='\171'}, {a='Test'}}}, eq({['\171']={{'\171'}, {['\171']='\171'}, {a='Test'}}},
meths.get_var('NESTEDVAR')) meths.get_var('NESTEDVAR'))
end) end)
it('errors and writes when a funcref is stored in a variable',
function()
nvim_command('let F = function("tr")')
meths.set_var('U', '10')
nvim_command('set shada+=!')
set_additional_cmd('set shada+=!')
eq('Vim(wshada):E475: Invalid argument: attempt to dump function reference',
exc_exec('wshada'))
meths.set_option('shada', '')
reset()
eq('10', meths.get_var('U'))
end)
it('errors and writes when a self-referencing list is stored in a variable',
function()
meths.set_var('L', {})
nvim_command('call add(L, L)')
meths.set_var('U', '10')
nvim_command('set shada+=!')
eq('Vim(wshada):E475: Invalid argument: container references itself',
exc_exec('wshada'))
meths.set_option('shada', '')
set_additional_cmd('set shada+=!')
reset()
eq('10', meths.get_var('U'))
end)
end) end)