diff options
author | Bram Moolenaar <Bram@vim.org> | 2015-02-03 12:55:18 +0100 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2015-02-03 12:55:18 +0100 |
commit | 2459a5ecaa43c8549ea53e9364253ff891676da5 (patch) | |
tree | b503323856e11d0e1e47e6c81875d32b1541ee04 /src/eval.c | |
parent | 4ac163ae5f137af236931e778660cf7878d70c25 (diff) | |
download | vim-git-2459a5ecaa43c8549ea53e9364253ff891676da5.tar.gz |
updated for version 7.4.609v7.4.609
Problem: For complicated list and dict use the garbage collector can run
out of stack space.
Solution: Use a stack of dicts and lists to be marked, thus making it
iterative instead of recursive. (Ben Fritz)
Diffstat (limited to 'src/eval.c')
-rw-r--r-- | src/eval.c | 223 |
1 files changed, 166 insertions, 57 deletions
diff --git a/src/eval.c b/src/eval.c index c9c179a05..6d5d762f2 100644 --- a/src/eval.c +++ b/src/eval.c @@ -93,7 +93,6 @@ typedef struct lval_S char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */ } lval_T; - static char *e_letunexp = N_("E18: Unexpected characters in :let"); static char *e_listidx = N_("E684: list index out of range: %ld"); static char *e_undefvar = N_("E121: Undefined variable: %s"); @@ -6811,6 +6810,7 @@ list_join(gap, l, sep, echo_style, copyID) garbage_collect() { int copyID; + int abort = FALSE; buf_T *buf; win_T *wp; int i; @@ -6841,82 +6841,95 @@ garbage_collect() * the item is referenced elsewhere the funccal must not be freed. */ for (fc = previous_funccal; fc != NULL; fc = fc->caller) { - set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1); - set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1); + abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID + 1, + NULL); + abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID + 1, + NULL); } /* script-local variables */ for (i = 1; i <= ga_scripts.ga_len; ++i) - set_ref_in_ht(&SCRIPT_VARS(i), copyID); + abort = abort || set_ref_in_ht(&SCRIPT_VARS(i), copyID, NULL); /* buffer-local variables */ for (buf = firstbuf; buf != NULL; buf = buf->b_next) - set_ref_in_item(&buf->b_bufvar.di_tv, copyID); + abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID, + NULL, NULL); /* window-local variables */ FOR_ALL_TAB_WINDOWS(tp, wp) - set_ref_in_item(&wp->w_winvar.di_tv, copyID); + abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID, + NULL, NULL); #ifdef FEAT_AUTOCMD if (aucmd_win != NULL) - set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID); + abort = abort || set_ref_in_item(&aucmd_win->w_winvar.di_tv, copyID, + NULL, NULL); #endif #ifdef FEAT_WINDOWS /* tabpage-local variables */ for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) - set_ref_in_item(&tp->tp_winvar.di_tv, copyID); + abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID, + NULL, NULL); #endif /* global variables */ - set_ref_in_ht(&globvarht, copyID); + abort = abort || set_ref_in_ht(&globvarht, copyID, NULL); /* function-local variables */ for (fc = current_funccal; fc != NULL; fc = fc->caller) { - set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID); - set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID); + abort = abort || set_ref_in_ht(&fc->l_vars.dv_hashtab, copyID, NULL); + abort = abort || set_ref_in_ht(&fc->l_avars.dv_hashtab, copyID, NULL); } /* v: vars */ - set_ref_in_ht(&vimvarht, copyID); + abort = abort || set_ref_in_ht(&vimvarht, copyID, NULL); #ifdef FEAT_LUA - set_ref_in_lua(copyID); + abort = abort || set_ref_in_lua(copyID); #endif #ifdef FEAT_PYTHON - set_ref_in_python(copyID); + abort = abort || set_ref_in_python(copyID); #endif #ifdef FEAT_PYTHON3 - set_ref_in_python3(copyID); + abort = abort || set_ref_in_python3(copyID); #endif - /* - * 2. Free lists and dictionaries that are not referenced. - */ - did_free = free_unref_items(copyID); - - /* - * 3. Check if any funccal can be freed now. - */ - for (pfc = &previous_funccal; *pfc != NULL; ) + if (!abort) { - if (can_free_funccal(*pfc, copyID)) + /* + * 2. Free lists and dictionaries that are not referenced. + */ + did_free = free_unref_items(copyID); + + /* + * 3. Check if any funccal can be freed now. + */ + for (pfc = &previous_funccal; *pfc != NULL; ) { - fc = *pfc; - *pfc = fc->caller; - free_funccal(fc, TRUE); - did_free = TRUE; - did_free_funccal = TRUE; + if (can_free_funccal(*pfc, copyID)) + { + fc = *pfc; + *pfc = fc->caller; + free_funccal(fc, TRUE); + did_free = TRUE; + did_free_funccal = TRUE; + } + else + pfc = &(*pfc)->caller; } - else - pfc = &(*pfc)->caller; + if (did_free_funccal) + /* When a funccal was freed some more items might be garbage + * collected, so run again. */ + (void)garbage_collect(); + } + else if (p_verbose > 0) + { + verb_msg((char_u *)_("Not enough memory to set references, garbage collection aborted!")); } - if (did_free_funccal) - /* When a funccal was freed some more items might be garbage - * collected, so run again. */ - (void)garbage_collect(); return did_free; } @@ -6976,48 +6989,112 @@ free_unref_items(copyID) /* * Mark all lists and dicts referenced through hashtab "ht" with "copyID". + * "list_stack" is used to add lists to be marked. Can be NULL. + * + * Returns TRUE if setting references failed somehow. */ - void -set_ref_in_ht(ht, copyID) - hashtab_T *ht; - int copyID; + int +set_ref_in_ht(ht, copyID, list_stack) + hashtab_T *ht; + int copyID; + list_stack_T **list_stack; { int todo; + int abort = FALSE; hashitem_T *hi; + hashtab_T *cur_ht; + ht_stack_T *ht_stack = NULL; + ht_stack_T *tempitem; - todo = (int)ht->ht_used; - for (hi = ht->ht_array; todo > 0; ++hi) - if (!HASHITEM_EMPTY(hi)) + cur_ht = ht; + for (;;) + { + if (!abort) { - --todo; - set_ref_in_item(&HI2DI(hi)->di_tv, copyID); + /* Mark each item in the hashtab. If the item contains a hashtab + * it is added to ht_stack, if it contains a list it is added to + * list_stack. */ + todo = (int)cur_ht->ht_used; + for (hi = cur_ht->ht_array; todo > 0; ++hi) + if (!HASHITEM_EMPTY(hi)) + { + --todo; + abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID, + &ht_stack, list_stack); + } } + + if (ht_stack == NULL) + break; + + /* take an item from the stack */ + cur_ht = ht_stack->ht; + tempitem = ht_stack; + ht_stack = ht_stack->prev; + free(tempitem); + } + + return abort; } /* * Mark all lists and dicts referenced through list "l" with "copyID". + * "ht_stack" is used to add hashtabs to be marked. Can be NULL. + * + * Returns TRUE if setting references failed somehow. */ - void -set_ref_in_list(l, copyID) + int +set_ref_in_list(l, copyID, ht_stack) list_T *l; int copyID; + ht_stack_T **ht_stack; { - listitem_T *li; + listitem_T *li; + int abort = FALSE; + list_T *cur_l; + list_stack_T *list_stack = NULL; + list_stack_T *tempitem; - for (li = l->lv_first; li != NULL; li = li->li_next) - set_ref_in_item(&li->li_tv, copyID); + cur_l = l; + for (;;) + { + if (!abort) + /* Mark each item in the list. If the item contains a hashtab + * it is added to ht_stack, if it contains a list it is added to + * list_stack. */ + for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next) + abort = abort || set_ref_in_item(&li->li_tv, copyID, + ht_stack, &list_stack); + if (list_stack == NULL) + break; + + /* take an item from the stack */ + cur_l = list_stack->list; + tempitem = list_stack; + list_stack = list_stack->prev; + free(tempitem); + } + + return abort; } /* * Mark all lists and dicts referenced through typval "tv" with "copyID". + * "list_stack" is used to add lists to be marked. Can be NULL. + * "ht_stack" is used to add hashtabs to be marked. Can be NULL. + * + * Returns TRUE if setting references failed somehow. */ - void -set_ref_in_item(tv, copyID) - typval_T *tv; - int copyID; + int +set_ref_in_item(tv, copyID, ht_stack, list_stack) + typval_T *tv; + int copyID; + ht_stack_T **ht_stack; + list_stack_T **list_stack; { dict_T *dd; list_T *ll; + int abort = FALSE; switch (tv->v_type) { @@ -7027,7 +7104,23 @@ set_ref_in_item(tv, copyID) { /* Didn't see this dict yet. */ dd->dv_copyID = copyID; - set_ref_in_ht(&dd->dv_hashtab, copyID); + if (ht_stack == NULL) + { + abort = set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack); + } + else + { + ht_stack_T *newitem = (ht_stack_T*)malloc( + sizeof(ht_stack_T)); + if (newitem == NULL) + abort = TRUE; + else + { + newitem->ht = &dd->dv_hashtab; + newitem->prev = *ht_stack; + *ht_stack = newitem; + } + } } break; @@ -7037,11 +7130,27 @@ set_ref_in_item(tv, copyID) { /* Didn't see this list yet. */ ll->lv_copyID = copyID; - set_ref_in_list(ll, copyID); + if (list_stack == NULL) + { + abort = set_ref_in_list(ll, copyID, ht_stack); + } + else + { + list_stack_T *newitem = (list_stack_T*)malloc( + sizeof(list_stack_T)); + if (newitem == NULL) + abort = TRUE; + else + { + newitem->list = ll; + newitem->prev = *list_stack; + *list_stack = newitem; + } + } } break; } - return; + return abort; } /* |