blob: 54c744480498db8f88c36328b48baa01c4f9646d [file] [log] [blame]
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +02001/* vi:set ts=8 sts=4 sw=4 noet:
2 *
3 * VIM - Vi IMproved by Bram Moolenaar
4 *
5 * Do ":help uganda" in Vim to read copying and usage conditions.
6 * Do ":help credits" in Vim to see a list of people who contributed.
7 * See README.txt for an overview of the Vim source code.
8 */
9
10/*
11 * gc.c: Garbage Collection
12 */
13
14#include "vim.h"
15
16#if defined(FEAT_EVAL) || defined(PROTO)
17
18/*
19 * When recursively copying lists and dicts we need to remember which ones we
20 * have done to avoid endless recursiveness. This unique ID is used for that.
21 * The last bit is used for previous_funccal, ignored when comparing.
22 */
23static int current_copyID = 0;
24
25static int free_unref_items(int copyID);
26
27/*
28 * Return the next (unique) copy ID.
29 * Used for serializing nested structures.
30 */
31 int
32get_copyID(void)
33{
34 current_copyID += COPYID_INC;
35 return current_copyID;
36}
37
38/*
39 * Garbage collection for lists and dictionaries.
40 *
41 * We use reference counts to be able to free most items right away when they
42 * are no longer used. But for composite items it's possible that it becomes
43 * unused while the reference count is > 0: When there is a recursive
44 * reference. Example:
45 * :let l = [1, 2, 3]
46 * :let d = {9: l}
47 * :let l[1] = d
48 *
49 * Since this is quite unusual we handle this with garbage collection: every
50 * once in a while find out which lists and dicts are not referenced from any
51 * variable.
52 *
53 * Here is a good reference text about garbage collection (refers to Python
54 * but it applies to all reference-counting mechanisms):
55 * http://python.ca/nas/python/gc/
56 */
57
58/*
59 * Do garbage collection for lists and dicts.
60 * When "testing" is TRUE this is called from test_garbagecollect_now().
61 * Return TRUE if some memory was freed.
62 */
63 int
64garbage_collect(int testing)
65{
66 int copyID;
67 int abort = FALSE;
68 buf_T *buf;
69 win_T *wp;
70 int did_free = FALSE;
71 tabpage_T *tp;
72
73 if (!testing)
74 {
75 // Only do this once.
76 want_garbage_collect = FALSE;
77 may_garbage_collect = FALSE;
78 garbage_collect_at_exit = FALSE;
79 }
80
81 // The execution stack can grow big, limit the size.
82 if (exestack.ga_maxlen - exestack.ga_len > 500)
83 {
84 size_t new_len;
85 char_u *pp;
86 int n;
87
88 // Keep 150% of the current size, with a minimum of the growth size.
89 n = exestack.ga_len / 2;
90 if (n < exestack.ga_growsize)
91 n = exestack.ga_growsize;
92
93 // Don't make it bigger though.
94 if (exestack.ga_len + n < exestack.ga_maxlen)
95 {
96 new_len = (size_t)exestack.ga_itemsize * (exestack.ga_len + n);
97 pp = vim_realloc(exestack.ga_data, new_len);
98 if (pp == NULL)
99 return FAIL;
100 exestack.ga_maxlen = exestack.ga_len + n;
101 exestack.ga_data = pp;
102 }
103 }
104
105 // We advance by two because we add one for items referenced through
106 // previous_funccal.
107 copyID = get_copyID();
108
109 /*
110 * 1. Go through all accessible variables and mark all lists and dicts
111 * with copyID.
112 */
113
114 // Don't free variables in the previous_funccal list unless they are only
115 // referenced through previous_funccal. This must be first, because if
116 // the item is referenced elsewhere the funccal must not be freed.
117 abort = abort || set_ref_in_previous_funccal(copyID);
118
119 // script-local variables
120 abort = abort || garbage_collect_scriptvars(copyID);
121
122 // buffer-local variables
123 FOR_ALL_BUFFERS(buf)
124 abort = abort || set_ref_in_item(&buf->b_bufvar.di_tv, copyID,
125 NULL, NULL);
126
127 // window-local variables
128 FOR_ALL_TAB_WINDOWS(tp, wp)
129 abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
130 NULL, NULL);
131 // window-local variables in autocmd windows
132 for (int i = 0; i < AUCMD_WIN_COUNT; ++i)
133 if (aucmd_win[i].auc_win != NULL)
134 abort = abort || set_ref_in_item(
135 &aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL);
136#ifdef FEAT_PROP_POPUP
137 FOR_ALL_POPUPWINS(wp)
138 abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
139 NULL, NULL);
140 FOR_ALL_TABPAGES(tp)
141 FOR_ALL_POPUPWINS_IN_TAB(tp, wp)
142 abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
143 NULL, NULL);
144#endif
145
146 // tabpage-local variables
147 FOR_ALL_TABPAGES(tp)
148 abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID,
149 NULL, NULL);
150 // global variables
151 abort = abort || garbage_collect_globvars(copyID);
152
153 // function-local variables
154 abort = abort || set_ref_in_call_stack(copyID);
155
156 // named functions (matters for closures)
157 abort = abort || set_ref_in_functions(copyID);
158
159 // function call arguments, if v:testing is set.
160 abort = abort || set_ref_in_func_args(copyID);
161
162 // funcstacks keep variables for closures
163 abort = abort || set_ref_in_funcstacks(copyID);
164
165 // loopvars keep variables for loop blocks
166 abort = abort || set_ref_in_loopvars(copyID);
167
168 // v: vars
169 abort = abort || garbage_collect_vimvars(copyID);
170
171 // callbacks in buffers
172 abort = abort || set_ref_in_buffers(copyID);
173
174 // 'completefunc', 'omnifunc' and 'thesaurusfunc' callbacks
175 abort = abort || set_ref_in_insexpand_funcs(copyID);
176
177 // 'operatorfunc' callback
178 abort = abort || set_ref_in_opfunc(copyID);
179
180 // 'tagfunc' callback
181 abort = abort || set_ref_in_tagfunc(copyID);
182
183 // 'imactivatefunc' and 'imstatusfunc' callbacks
184 abort = abort || set_ref_in_im_funcs(copyID);
185
Yegappan Lakshmanana13f3a42024-11-02 18:40:10 +0100186 // 'findfunc' callback
187 abort = abort || set_ref_in_findfunc(copyID);
188
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200189#ifdef FEAT_LUA
190 abort = abort || set_ref_in_lua(copyID);
191#endif
192
193#ifdef FEAT_PYTHON
194 abort = abort || set_ref_in_python(copyID);
195#endif
196
197#ifdef FEAT_PYTHON3
198 abort = abort || set_ref_in_python3(copyID);
199#endif
200
201#ifdef FEAT_JOB_CHANNEL
202 abort = abort || set_ref_in_channel(copyID);
203 abort = abort || set_ref_in_job(copyID);
204#endif
205#ifdef FEAT_NETBEANS_INTG
206 abort = abort || set_ref_in_nb_channel(copyID);
207#endif
208
209#ifdef FEAT_TIMERS
210 abort = abort || set_ref_in_timer(copyID);
211#endif
212
213#ifdef FEAT_QUICKFIX
214 abort = abort || set_ref_in_quickfix(copyID);
215#endif
216
217#ifdef FEAT_TERMINAL
218 abort = abort || set_ref_in_term(copyID);
219#endif
220
221#ifdef FEAT_PROP_POPUP
222 abort = abort || set_ref_in_popups(copyID);
223#endif
224
225 abort = abort || set_ref_in_classes(copyID);
226
227 if (!abort)
228 {
229 /*
230 * 2. Free lists and dictionaries that are not referenced.
231 */
232 did_free = free_unref_items(copyID);
233
234 /*
235 * 3. Check if any funccal can be freed now.
236 * This may call us back recursively.
237 */
238 free_unref_funccal(copyID, testing);
239 }
240 else if (p_verbose > 0)
241 {
242 verb_msg(_("Not enough memory to set references, garbage collection aborted!"));
243 }
244
245 return did_free;
246}
247
248/*
249 * Free lists, dictionaries, channels and jobs that are no longer referenced.
250 */
251 static int
252free_unref_items(int copyID)
253{
254 int did_free = FALSE;
255
256 // Let all "free" functions know that we are here. This means no
257 // dictionaries, lists, channels or jobs are to be freed, because we will
258 // do that here.
259 in_free_unref_items = TRUE;
260
261 /*
262 * PASS 1: free the contents of the items. We don't free the items
263 * themselves yet, so that it is possible to decrement refcount counters
264 */
265
266 // Go through the list of dicts and free items without this copyID.
267 did_free |= dict_free_nonref(copyID);
268
269 // Go through the list of lists and free items without this copyID.
270 did_free |= list_free_nonref(copyID);
271
272 // Go through the list of objects and free items without this copyID.
273 did_free |= object_free_nonref(copyID);
274
275 // Go through the list of classes and free items without this copyID.
276 did_free |= class_free_nonref(copyID);
277
278#ifdef FEAT_JOB_CHANNEL
279 // Go through the list of jobs and free items without the copyID. This
280 // must happen before doing channels, because jobs refer to channels, but
281 // the reference from the channel to the job isn't tracked.
282 did_free |= free_unused_jobs_contents(copyID, COPYID_MASK);
283
284 // Go through the list of channels and free items without the copyID.
285 did_free |= free_unused_channels_contents(copyID, COPYID_MASK);
286#endif
287
288 /*
289 * PASS 2: free the items themselves.
290 */
291 object_free_items(copyID);
292 dict_free_items(copyID);
293 list_free_items(copyID);
294
295#ifdef FEAT_JOB_CHANNEL
296 // Go through the list of jobs and free items without the copyID. This
297 // must happen before doing channels, because jobs refer to channels, but
298 // the reference from the channel to the job isn't tracked.
299 free_unused_jobs(copyID, COPYID_MASK);
300
301 // Go through the list of channels and free items without the copyID.
302 free_unused_channels(copyID, COPYID_MASK);
303#endif
304
305 in_free_unref_items = FALSE;
306
307 return did_free;
308}
309
310/*
311 * Mark all lists and dicts referenced through hashtab "ht" with "copyID".
312 * "list_stack" is used to add lists to be marked. Can be NULL.
313 *
314 * Returns TRUE if setting references failed somehow.
315 */
316 int
317set_ref_in_ht(hashtab_T *ht, int copyID, list_stack_T **list_stack)
318{
319 int todo;
320 int abort = FALSE;
321 hashitem_T *hi;
322 hashtab_T *cur_ht;
323 ht_stack_T *ht_stack = NULL;
324 ht_stack_T *tempitem;
325
326 cur_ht = ht;
327 for (;;)
328 {
329 if (!abort)
330 {
331 // Mark each item in the hashtab. If the item contains a hashtab
332 // it is added to ht_stack, if it contains a list it is added to
333 // list_stack.
334 todo = (int)cur_ht->ht_used;
335 FOR_ALL_HASHTAB_ITEMS(cur_ht, hi, todo)
336 if (!HASHITEM_EMPTY(hi))
337 {
338 --todo;
339 abort = abort || set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
340 &ht_stack, list_stack);
341 }
342 }
343
344 if (ht_stack == NULL)
345 break;
346
347 // take an item from the stack
348 cur_ht = ht_stack->ht;
349 tempitem = ht_stack;
350 ht_stack = ht_stack->prev;
351 free(tempitem);
352 }
353
354 return abort;
355}
356
357#if defined(FEAT_LUA) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \
358 || defined(PROTO)
359/*
360 * Mark a dict and its items with "copyID".
361 * Returns TRUE if setting references failed somehow.
362 */
363 int
364set_ref_in_dict(dict_T *d, int copyID)
365{
366 if (d != NULL && d->dv_copyID != copyID)
367 {
368 d->dv_copyID = copyID;
369 return set_ref_in_ht(&d->dv_hashtab, copyID, NULL);
370 }
371 return FALSE;
372}
373#endif
374
375/*
376 * Mark a list and its items with "copyID".
377 * Returns TRUE if setting references failed somehow.
378 */
379 int
380set_ref_in_list(list_T *ll, int copyID)
381{
382 if (ll != NULL && ll->lv_copyID != copyID)
383 {
384 ll->lv_copyID = copyID;
385 return set_ref_in_list_items(ll, copyID, NULL);
386 }
387 return FALSE;
388}
389
390/*
391 * Mark all lists and dicts referenced through list "l" with "copyID".
392 * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
393 *
394 * Returns TRUE if setting references failed somehow.
395 */
396 int
397set_ref_in_list_items(list_T *l, int copyID, ht_stack_T **ht_stack)
398{
399 listitem_T *li;
400 int abort = FALSE;
401 list_T *cur_l;
402 list_stack_T *list_stack = NULL;
403 list_stack_T *tempitem;
404
405 cur_l = l;
406 for (;;)
407 {
408 if (!abort && cur_l->lv_first != &range_list_item)
409 // Mark each item in the list. If the item contains a hashtab
410 // it is added to ht_stack, if it contains a list it is added to
411 // list_stack.
412 for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next)
413 abort = abort || set_ref_in_item(&li->li_tv, copyID,
414 ht_stack, &list_stack);
415 if (list_stack == NULL)
416 break;
417
418 // take an item from the stack
419 cur_l = list_stack->list;
420 tempitem = list_stack;
421 list_stack = list_stack->prev;
422 free(tempitem);
423 }
424
425 return abort;
426}
427
428/*
429 * Mark the partial in callback 'cb' with "copyID".
430 */
431 int
432set_ref_in_callback(callback_T *cb, int copyID)
433{
434 typval_T tv;
435
436 if (cb->cb_name == NULL || *cb->cb_name == NUL || cb->cb_partial == NULL)
437 return FALSE;
438
439 tv.v_type = VAR_PARTIAL;
440 tv.vval.v_partial = cb->cb_partial;
441 return set_ref_in_item(&tv, copyID, NULL, NULL);
442}
443
444/*
445 * Mark the dict "dd" with "copyID".
446 * Also see set_ref_in_item().
447 */
448 static int
449set_ref_in_item_dict(
450 dict_T *dd,
451 int copyID,
452 ht_stack_T **ht_stack,
453 list_stack_T **list_stack)
454{
455 if (dd == NULL || dd->dv_copyID == copyID)
456 return FALSE;
457
458 // Didn't see this dict yet.
459 dd->dv_copyID = copyID;
460 if (ht_stack == NULL)
461 return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack);
462
463 ht_stack_T *newitem = ALLOC_ONE(ht_stack_T);
464 if (newitem == NULL)
465 return TRUE;
466
467 newitem->ht = &dd->dv_hashtab;
468 newitem->prev = *ht_stack;
469 *ht_stack = newitem;
470
471 return FALSE;
472}
473
474/*
475 * Mark the list "ll" with "copyID".
476 * Also see set_ref_in_item().
477 */
478 static int
479set_ref_in_item_list(
480 list_T *ll,
481 int copyID,
482 ht_stack_T **ht_stack,
483 list_stack_T **list_stack)
484{
485 if (ll == NULL || ll->lv_copyID == copyID)
486 return FALSE;
487
488 // Didn't see this list yet.
489 ll->lv_copyID = copyID;
490 if (list_stack == NULL)
491 return set_ref_in_list_items(ll, copyID, ht_stack);
492
493 list_stack_T *newitem = ALLOC_ONE(list_stack_T);
494 if (newitem == NULL)
495 return TRUE;
496
497 newitem->list = ll;
498 newitem->prev = *list_stack;
499 *list_stack = newitem;
500
501 return FALSE;
502}
503
504/*
505 * Mark the partial "pt" with "copyID".
506 * Also see set_ref_in_item().
507 */
508 static int
509set_ref_in_item_partial(
510 partial_T *pt,
511 int copyID,
512 ht_stack_T **ht_stack,
513 list_stack_T **list_stack)
514{
515 if (pt == NULL || pt->pt_copyID == copyID)
516 return FALSE;
517
518 // Didn't see this partial yet.
519 pt->pt_copyID = copyID;
520
521 int abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
522
523 if (pt->pt_dict != NULL)
524 {
525 typval_T dtv;
526
527 dtv.v_type = VAR_DICT;
528 dtv.vval.v_dict = pt->pt_dict;
529 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
530 }
531
532 if (pt->pt_obj != NULL)
533 {
534 typval_T objtv;
535
536 objtv.v_type = VAR_OBJECT;
537 objtv.vval.v_object = pt->pt_obj;
538 set_ref_in_item(&objtv, copyID, ht_stack, list_stack);
539 }
540
541 for (int i = 0; i < pt->pt_argc; ++i)
542 abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
543 ht_stack, list_stack);
544 // pt_funcstack is handled in set_ref_in_funcstacks()
545 // pt_loopvars is handled in set_ref_in_loopvars()
546
547 return abort;
548}
549
550#ifdef FEAT_JOB_CHANNEL
551/*
552 * Mark the job "pt" with "copyID".
553 * Also see set_ref_in_item().
554 */
555 static int
556set_ref_in_item_job(
557 job_T *job,
558 int copyID,
559 ht_stack_T **ht_stack,
560 list_stack_T **list_stack)
561{
562 typval_T dtv;
563
564 if (job == NULL || job->jv_copyID == copyID)
565 return FALSE;
566
567 job->jv_copyID = copyID;
568 if (job->jv_channel != NULL)
569 {
570 dtv.v_type = VAR_CHANNEL;
571 dtv.vval.v_channel = job->jv_channel;
572 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
573 }
574 if (job->jv_exit_cb.cb_partial != NULL)
575 {
576 dtv.v_type = VAR_PARTIAL;
577 dtv.vval.v_partial = job->jv_exit_cb.cb_partial;
578 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
579 }
580
581 return FALSE;
582}
583
584/*
585 * Mark the channel "ch" with "copyID".
586 * Also see set_ref_in_item().
587 */
588 static int
589set_ref_in_item_channel(
590 channel_T *ch,
591 int copyID,
592 ht_stack_T **ht_stack,
593 list_stack_T **list_stack)
594{
595 typval_T dtv;
596
597 if (ch == NULL || ch->ch_copyID == copyID)
598 return FALSE;
599
600 ch->ch_copyID = copyID;
601 for (ch_part_T part = PART_SOCK; part < PART_COUNT; ++part)
602 {
603 for (jsonq_T *jq = ch->ch_part[part].ch_json_head.jq_next;
604 jq != NULL; jq = jq->jq_next)
605 set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack);
606 for (cbq_T *cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL;
607 cq = cq->cq_next)
608 if (cq->cq_callback.cb_partial != NULL)
609 {
610 dtv.v_type = VAR_PARTIAL;
611 dtv.vval.v_partial = cq->cq_callback.cb_partial;
612 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
613 }
614 if (ch->ch_part[part].ch_callback.cb_partial != NULL)
615 {
616 dtv.v_type = VAR_PARTIAL;
617 dtv.vval.v_partial = ch->ch_part[part].ch_callback.cb_partial;
618 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
619 }
620 }
621 if (ch->ch_callback.cb_partial != NULL)
622 {
623 dtv.v_type = VAR_PARTIAL;
624 dtv.vval.v_partial = ch->ch_callback.cb_partial;
625 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
626 }
627 if (ch->ch_close_cb.cb_partial != NULL)
628 {
629 dtv.v_type = VAR_PARTIAL;
630 dtv.vval.v_partial = ch->ch_close_cb.cb_partial;
631 set_ref_in_item(&dtv, copyID, ht_stack, list_stack);
632 }
633
634 return FALSE;
635}
636#endif
637
638/*
639 * Mark the class "cl" with "copyID".
640 * Also see set_ref_in_item().
641 */
642 int
643set_ref_in_item_class(
644 class_T *cl,
645 int copyID,
646 ht_stack_T **ht_stack,
647 list_stack_T **list_stack)
648{
649 int abort = FALSE;
650
651 if (cl == NULL || cl->class_copyID == copyID)
652 return FALSE;
653
654 cl->class_copyID = copyID;
655 if (cl->class_members_tv != NULL)
656 {
657 // The "class_members_tv" table is allocated only for regular classes
658 // and not for interfaces.
659 for (int i = 0; !abort && i < cl->class_class_member_count; ++i)
660 abort = abort || set_ref_in_item(
661 &cl->class_members_tv[i],
662 copyID, ht_stack, list_stack);
663 }
664
665 for (int i = 0; !abort && i < cl->class_class_function_count; ++i)
666 abort = abort || set_ref_in_func(NULL,
667 cl->class_class_functions[i], copyID);
668
669 for (int i = 0; !abort && i < cl->class_obj_method_count; ++i)
670 abort = abort || set_ref_in_func(NULL,
671 cl->class_obj_methods[i], copyID);
672
673 return abort;
674}
675
676/*
677 * Mark the object "cl" with "copyID".
678 * Also see set_ref_in_item().
679 */
680 static int
681set_ref_in_item_object(
682 object_T *obj,
683 int copyID,
684 ht_stack_T **ht_stack,
685 list_stack_T **list_stack)
686{
687 int abort = FALSE;
688
689 if (obj == NULL || obj->obj_copyID == copyID)
690 return FALSE;
691
692 obj->obj_copyID = copyID;
693
694 // The typval_T array is right after the object_T.
695 typval_T *mtv = (typval_T *)(obj + 1);
696 for (int i = 0; !abort
697 && i < obj->obj_class->class_obj_member_count; ++i)
698 abort = abort || set_ref_in_item(mtv + i, copyID,
699 ht_stack, list_stack);
700
701 return abort;
702}
703
704/*
705 * Mark all lists, dicts and other container types referenced through typval
706 * "tv" with "copyID".
707 * "list_stack" is used to add lists to be marked. Can be NULL.
708 * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
709 *
710 * Returns TRUE if setting references failed somehow.
711 */
712 int
713set_ref_in_item(
714 typval_T *tv,
715 int copyID,
716 ht_stack_T **ht_stack,
717 list_stack_T **list_stack)
718{
719 int abort = FALSE;
720
721 switch (tv->v_type)
722 {
723 case VAR_DICT:
724 return set_ref_in_item_dict(tv->vval.v_dict, copyID,
725 ht_stack, list_stack);
726
727 case VAR_LIST:
728 return set_ref_in_item_list(tv->vval.v_list, copyID,
729 ht_stack, list_stack);
730
731 case VAR_FUNC:
732 {
733 abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
734 break;
735 }
736
737 case VAR_PARTIAL:
738 return set_ref_in_item_partial(tv->vval.v_partial, copyID,
739 ht_stack, list_stack);
740
741 case VAR_JOB:
742#ifdef FEAT_JOB_CHANNEL
743 return set_ref_in_item_job(tv->vval.v_job, copyID,
744 ht_stack, list_stack);
745#else
746 break;
747#endif
748
749 case VAR_CHANNEL:
750#ifdef FEAT_JOB_CHANNEL
751 return set_ref_in_item_channel(tv->vval.v_channel, copyID,
752 ht_stack, list_stack);
753#else
754 break;
755#endif
756
757 case VAR_CLASS:
758 return set_ref_in_item_class(tv->vval.v_class, copyID,
759 ht_stack, list_stack);
760
761 case VAR_OBJECT:
762 return set_ref_in_item_object(tv->vval.v_object, copyID,
763 ht_stack, list_stack);
764
765 case VAR_UNKNOWN:
766 case VAR_ANY:
767 case VAR_VOID:
768 case VAR_BOOL:
769 case VAR_SPECIAL:
770 case VAR_NUMBER:
771 case VAR_FLOAT:
772 case VAR_STRING:
773 case VAR_BLOB:
774 case VAR_TYPEALIAS:
775 case VAR_INSTR:
776 // Types that do not contain any other item
777 break;
778 }
779
780 return abort;
781}
782
783#endif