blob: c8aa5faa8ad206f4c40a0e18e1d0191d231bb1a8 [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,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100125 NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200126
127 // window-local variables
128 FOR_ALL_TAB_WINDOWS(tp, wp)
129 abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100130 NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200131 // 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(
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100135 &aucmd_win[i].auc_win->w_winvar.di_tv, copyID, NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200136#ifdef FEAT_PROP_POPUP
137 FOR_ALL_POPUPWINS(wp)
138 abort = abort || set_ref_in_item(&wp->w_winvar.di_tv, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100139 NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200140 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,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100143 NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200144#endif
145
146 // tabpage-local variables
147 FOR_ALL_TABPAGES(tp)
148 abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100149 NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200150 // 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
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100272 // Go through the list of tuples and free items without this copyID.
273 did_free |= tuple_free_nonref(copyID);
274
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200275 // Go through the list of objects and free items without this copyID.
276 did_free |= object_free_nonref(copyID);
277
278 // Go through the list of classes and free items without this copyID.
279 did_free |= class_free_nonref(copyID);
280
281#ifdef FEAT_JOB_CHANNEL
282 // Go through the list of jobs and free items without the copyID. This
283 // must happen before doing channels, because jobs refer to channels, but
284 // the reference from the channel to the job isn't tracked.
285 did_free |= free_unused_jobs_contents(copyID, COPYID_MASK);
286
287 // Go through the list of channels and free items without the copyID.
288 did_free |= free_unused_channels_contents(copyID, COPYID_MASK);
289#endif
290
291 /*
292 * PASS 2: free the items themselves.
293 */
294 object_free_items(copyID);
295 dict_free_items(copyID);
296 list_free_items(copyID);
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100297 tuple_free_items(copyID);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200298
299#ifdef FEAT_JOB_CHANNEL
300 // Go through the list of jobs and free items without the copyID. This
301 // must happen before doing channels, because jobs refer to channels, but
302 // the reference from the channel to the job isn't tracked.
303 free_unused_jobs(copyID, COPYID_MASK);
304
305 // Go through the list of channels and free items without the copyID.
306 free_unused_channels(copyID, COPYID_MASK);
307#endif
308
309 in_free_unref_items = FALSE;
310
311 return did_free;
312}
313
314/*
315 * Mark all lists and dicts referenced through hashtab "ht" with "copyID".
316 * "list_stack" is used to add lists to be marked. Can be NULL.
317 *
318 * Returns TRUE if setting references failed somehow.
319 */
320 int
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100321set_ref_in_ht(
322 hashtab_T *ht,
323 int copyID,
324 list_stack_T **list_stack,
325 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200326{
327 int todo;
328 int abort = FALSE;
329 hashitem_T *hi;
330 hashtab_T *cur_ht;
331 ht_stack_T *ht_stack = NULL;
332 ht_stack_T *tempitem;
333
334 cur_ht = ht;
335 for (;;)
336 {
337 if (!abort)
338 {
339 // Mark each item in the hashtab. If the item contains a hashtab
340 // it is added to ht_stack, if it contains a list it is added to
341 // list_stack.
342 todo = (int)cur_ht->ht_used;
343 FOR_ALL_HASHTAB_ITEMS(cur_ht, hi, todo)
344 if (!HASHITEM_EMPTY(hi))
345 {
346 --todo;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100347 abort = abort
348 || set_ref_in_item(&HI2DI(hi)->di_tv, copyID,
349 &ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200350 }
351 }
352
353 if (ht_stack == NULL)
354 break;
355
356 // take an item from the stack
357 cur_ht = ht_stack->ht;
358 tempitem = ht_stack;
359 ht_stack = ht_stack->prev;
360 free(tempitem);
361 }
362
363 return abort;
364}
365
366#if defined(FEAT_LUA) || defined(FEAT_PYTHON) || defined(FEAT_PYTHON3) \
367 || defined(PROTO)
368/*
369 * Mark a dict and its items with "copyID".
370 * Returns TRUE if setting references failed somehow.
371 */
372 int
373set_ref_in_dict(dict_T *d, int copyID)
374{
375 if (d != NULL && d->dv_copyID != copyID)
376 {
377 d->dv_copyID = copyID;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100378 return set_ref_in_ht(&d->dv_hashtab, copyID, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200379 }
380 return FALSE;
381}
382#endif
383
384/*
385 * Mark a list and its items with "copyID".
386 * Returns TRUE if setting references failed somehow.
387 */
388 int
389set_ref_in_list(list_T *ll, int copyID)
390{
391 if (ll != NULL && ll->lv_copyID != copyID)
392 {
393 ll->lv_copyID = copyID;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100394 return set_ref_in_list_items(ll, copyID, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200395 }
396 return FALSE;
397}
398
399/*
400 * Mark all lists and dicts referenced through list "l" with "copyID".
401 * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
402 *
403 * Returns TRUE if setting references failed somehow.
404 */
405 int
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100406set_ref_in_list_items(
407 list_T *l,
408 int copyID,
409 ht_stack_T **ht_stack,
410 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200411{
412 listitem_T *li;
413 int abort = FALSE;
414 list_T *cur_l;
415 list_stack_T *list_stack = NULL;
416 list_stack_T *tempitem;
417
418 cur_l = l;
419 for (;;)
420 {
421 if (!abort && cur_l->lv_first != &range_list_item)
422 // Mark each item in the list. If the item contains a hashtab
423 // it is added to ht_stack, if it contains a list it is added to
424 // list_stack.
425 for (li = cur_l->lv_first; !abort && li != NULL; li = li->li_next)
426 abort = abort || set_ref_in_item(&li->li_tv, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100427 ht_stack, &list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200428 if (list_stack == NULL)
429 break;
430
431 // take an item from the stack
432 cur_l = list_stack->list;
433 tempitem = list_stack;
434 list_stack = list_stack->prev;
435 free(tempitem);
436 }
437
438 return abort;
439}
440
441/*
Yegappan Lakshmanan038be272025-03-26 18:46:21 +0100442 * Mark a tuple and its items with "copyID".
443 * Returns TRUE if setting references failed somehow.
444 */
445 int
446set_ref_in_tuple(tuple_T *tuple, int copyID)
447{
448 if (tuple != NULL && tuple->tv_copyID != copyID)
449 {
450 tuple->tv_copyID = copyID;
451 return set_ref_in_tuple_items(tuple, copyID, NULL, NULL);
452 }
453 return FALSE;
454}
455
456/*
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100457 * Mark all lists and dicts referenced through tuple "t" with "copyID".
458 * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
459 *
460 * Returns TRUE if setting references failed somehow.
461 */
462 int
463set_ref_in_tuple_items(
464 tuple_T *tuple,
465 int copyID,
466 ht_stack_T **ht_stack,
467 list_stack_T **list_stack)
468{
469 int abort = FALSE;
470 tuple_T *cur_t;
471 tuple_stack_T *tuple_stack = NULL;
472 tuple_stack_T *tempitem;
473
474 cur_t = tuple;
475 for (;;)
476 {
477 // Mark each item in the tuple. If the item contains a hashtab
478 // it is added to ht_stack, if it contains a list it is added to
479 // list_stack.
480 for (int i = 0; i < cur_t->tv_items.ga_len; i++)
481 {
482 typval_T *tv = ((typval_T *)cur_t->tv_items.ga_data) + i;
483 abort = abort
484 || set_ref_in_item(tv, copyID,
485 ht_stack, list_stack, &tuple_stack);
486 }
487 if (tuple_stack == NULL)
488 break;
489
490 // take an item from the stack
491 cur_t = tuple_stack->tuple;
492 tempitem = tuple_stack;
493 tuple_stack = tuple_stack->prev;
494 free(tempitem);
495 }
496
497 return abort;
498}
499
500/*
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200501 * Mark the partial in callback 'cb' with "copyID".
502 */
503 int
504set_ref_in_callback(callback_T *cb, int copyID)
505{
506 typval_T tv;
507
508 if (cb->cb_name == NULL || *cb->cb_name == NUL || cb->cb_partial == NULL)
509 return FALSE;
510
511 tv.v_type = VAR_PARTIAL;
512 tv.vval.v_partial = cb->cb_partial;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100513 return set_ref_in_item(&tv, copyID, NULL, NULL, NULL);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200514}
515
516/*
517 * Mark the dict "dd" with "copyID".
518 * Also see set_ref_in_item().
519 */
520 static int
521set_ref_in_item_dict(
522 dict_T *dd,
523 int copyID,
524 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100525 list_stack_T **list_stack,
526 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200527{
528 if (dd == NULL || dd->dv_copyID == copyID)
529 return FALSE;
530
531 // Didn't see this dict yet.
532 dd->dv_copyID = copyID;
533 if (ht_stack == NULL)
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100534 return set_ref_in_ht(&dd->dv_hashtab, copyID, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200535
536 ht_stack_T *newitem = ALLOC_ONE(ht_stack_T);
537 if (newitem == NULL)
538 return TRUE;
539
540 newitem->ht = &dd->dv_hashtab;
541 newitem->prev = *ht_stack;
542 *ht_stack = newitem;
543
544 return FALSE;
545}
546
547/*
548 * Mark the list "ll" with "copyID".
549 * Also see set_ref_in_item().
550 */
551 static int
552set_ref_in_item_list(
553 list_T *ll,
554 int copyID,
555 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100556 list_stack_T **list_stack,
557 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200558{
559 if (ll == NULL || ll->lv_copyID == copyID)
560 return FALSE;
561
562 // Didn't see this list yet.
563 ll->lv_copyID = copyID;
564 if (list_stack == NULL)
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100565 return set_ref_in_list_items(ll, copyID, ht_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200566
567 list_stack_T *newitem = ALLOC_ONE(list_stack_T);
568 if (newitem == NULL)
569 return TRUE;
570
571 newitem->list = ll;
572 newitem->prev = *list_stack;
573 *list_stack = newitem;
574
575 return FALSE;
576}
577
578/*
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100579 * Mark the tuple "tt" with "copyID".
580 * Also see set_ref_in_item().
581 */
582 static int
583set_ref_in_item_tuple(
584 tuple_T *tt,
585 int copyID,
586 ht_stack_T **ht_stack,
587 list_stack_T **list_stack,
588 tuple_stack_T **tuple_stack)
589{
590 if (tt == NULL || tt->tv_copyID == copyID)
591 return FALSE;
592
593 // Didn't see this tuple yet.
594 tt->tv_copyID = copyID;
595 if (tuple_stack == NULL)
596 return set_ref_in_tuple_items(tt, copyID, ht_stack, list_stack);
597
598 tuple_stack_T *newitem = ALLOC_ONE(tuple_stack_T);
599 if (newitem == NULL)
600 return TRUE;
601
602 newitem->tuple = tt;
603 newitem->prev = *tuple_stack;
604 *tuple_stack = newitem;
605
606 return FALSE;
607}
608
609/*
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200610 * Mark the partial "pt" with "copyID".
611 * Also see set_ref_in_item().
612 */
613 static int
614set_ref_in_item_partial(
615 partial_T *pt,
616 int copyID,
617 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100618 list_stack_T **list_stack,
619 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200620{
621 if (pt == NULL || pt->pt_copyID == copyID)
622 return FALSE;
623
624 // Didn't see this partial yet.
625 pt->pt_copyID = copyID;
626
627 int abort = set_ref_in_func(pt->pt_name, pt->pt_func, copyID);
628
629 if (pt->pt_dict != NULL)
630 {
631 typval_T dtv;
632
633 dtv.v_type = VAR_DICT;
634 dtv.vval.v_dict = pt->pt_dict;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100635 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200636 }
637
638 if (pt->pt_obj != NULL)
639 {
640 typval_T objtv;
641
642 objtv.v_type = VAR_OBJECT;
643 objtv.vval.v_object = pt->pt_obj;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100644 set_ref_in_item(&objtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200645 }
646
647 for (int i = 0; i < pt->pt_argc; ++i)
648 abort = abort || set_ref_in_item(&pt->pt_argv[i], copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100649 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200650 // pt_funcstack is handled in set_ref_in_funcstacks()
651 // pt_loopvars is handled in set_ref_in_loopvars()
652
653 return abort;
654}
655
656#ifdef FEAT_JOB_CHANNEL
657/*
658 * Mark the job "pt" with "copyID".
659 * Also see set_ref_in_item().
660 */
661 static int
662set_ref_in_item_job(
663 job_T *job,
664 int copyID,
665 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100666 list_stack_T **list_stack,
667 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200668{
669 typval_T dtv;
670
671 if (job == NULL || job->jv_copyID == copyID)
672 return FALSE;
673
674 job->jv_copyID = copyID;
675 if (job->jv_channel != NULL)
676 {
677 dtv.v_type = VAR_CHANNEL;
678 dtv.vval.v_channel = job->jv_channel;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100679 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200680 }
681 if (job->jv_exit_cb.cb_partial != NULL)
682 {
683 dtv.v_type = VAR_PARTIAL;
684 dtv.vval.v_partial = job->jv_exit_cb.cb_partial;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100685 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200686 }
687
688 return FALSE;
689}
690
691/*
692 * Mark the channel "ch" with "copyID".
693 * Also see set_ref_in_item().
694 */
695 static int
696set_ref_in_item_channel(
697 channel_T *ch,
698 int copyID,
699 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100700 list_stack_T **list_stack,
701 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200702{
703 typval_T dtv;
704
705 if (ch == NULL || ch->ch_copyID == copyID)
706 return FALSE;
707
708 ch->ch_copyID = copyID;
709 for (ch_part_T part = PART_SOCK; part < PART_COUNT; ++part)
710 {
711 for (jsonq_T *jq = ch->ch_part[part].ch_json_head.jq_next;
712 jq != NULL; jq = jq->jq_next)
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100713 set_ref_in_item(jq->jq_value, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200714 for (cbq_T *cq = ch->ch_part[part].ch_cb_head.cq_next; cq != NULL;
715 cq = cq->cq_next)
716 if (cq->cq_callback.cb_partial != NULL)
717 {
718 dtv.v_type = VAR_PARTIAL;
719 dtv.vval.v_partial = cq->cq_callback.cb_partial;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100720 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200721 }
722 if (ch->ch_part[part].ch_callback.cb_partial != NULL)
723 {
724 dtv.v_type = VAR_PARTIAL;
725 dtv.vval.v_partial = ch->ch_part[part].ch_callback.cb_partial;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100726 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200727 }
728 }
729 if (ch->ch_callback.cb_partial != NULL)
730 {
731 dtv.v_type = VAR_PARTIAL;
732 dtv.vval.v_partial = ch->ch_callback.cb_partial;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100733 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200734 }
735 if (ch->ch_close_cb.cb_partial != NULL)
736 {
737 dtv.v_type = VAR_PARTIAL;
738 dtv.vval.v_partial = ch->ch_close_cb.cb_partial;
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100739 set_ref_in_item(&dtv, copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200740 }
741
742 return FALSE;
743}
744#endif
745
746/*
747 * Mark the class "cl" with "copyID".
748 * Also see set_ref_in_item().
749 */
750 int
751set_ref_in_item_class(
752 class_T *cl,
753 int copyID,
754 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100755 list_stack_T **list_stack,
756 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200757{
758 int abort = FALSE;
759
760 if (cl == NULL || cl->class_copyID == copyID)
761 return FALSE;
762
763 cl->class_copyID = copyID;
764 if (cl->class_members_tv != NULL)
765 {
766 // The "class_members_tv" table is allocated only for regular classes
767 // and not for interfaces.
768 for (int i = 0; !abort && i < cl->class_class_member_count; ++i)
769 abort = abort || set_ref_in_item(
770 &cl->class_members_tv[i],
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100771 copyID, ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200772 }
773
774 for (int i = 0; !abort && i < cl->class_class_function_count; ++i)
775 abort = abort || set_ref_in_func(NULL,
776 cl->class_class_functions[i], copyID);
777
778 for (int i = 0; !abort && i < cl->class_obj_method_count; ++i)
779 abort = abort || set_ref_in_func(NULL,
780 cl->class_obj_methods[i], copyID);
781
782 return abort;
783}
784
785/*
786 * Mark the object "cl" with "copyID".
787 * Also see set_ref_in_item().
788 */
789 static int
790set_ref_in_item_object(
791 object_T *obj,
792 int copyID,
793 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100794 list_stack_T **list_stack,
795 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200796{
797 int abort = FALSE;
798
799 if (obj == NULL || obj->obj_copyID == copyID)
800 return FALSE;
801
802 obj->obj_copyID = copyID;
803
804 // The typval_T array is right after the object_T.
805 typval_T *mtv = (typval_T *)(obj + 1);
806 for (int i = 0; !abort
807 && i < obj->obj_class->class_obj_member_count; ++i)
808 abort = abort || set_ref_in_item(mtv + i, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100809 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200810
811 return abort;
812}
813
814/*
815 * Mark all lists, dicts and other container types referenced through typval
816 * "tv" with "copyID".
817 * "list_stack" is used to add lists to be marked. Can be NULL.
818 * "ht_stack" is used to add hashtabs to be marked. Can be NULL.
819 *
820 * Returns TRUE if setting references failed somehow.
821 */
822 int
823set_ref_in_item(
824 typval_T *tv,
825 int copyID,
826 ht_stack_T **ht_stack,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100827 list_stack_T **list_stack,
828 tuple_stack_T **tuple_stack)
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200829{
830 int abort = FALSE;
831
832 switch (tv->v_type)
833 {
834 case VAR_DICT:
835 return set_ref_in_item_dict(tv->vval.v_dict, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100836 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200837
838 case VAR_LIST:
839 return set_ref_in_item_list(tv->vval.v_list, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100840 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200841
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100842 case VAR_TUPLE:
843 return set_ref_in_item_tuple(tv->vval.v_tuple, copyID,
844 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200845 case VAR_FUNC:
846 {
847 abort = set_ref_in_func(tv->vval.v_string, NULL, copyID);
848 break;
849 }
850
851 case VAR_PARTIAL:
852 return set_ref_in_item_partial(tv->vval.v_partial, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100853 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200854
855 case VAR_JOB:
856#ifdef FEAT_JOB_CHANNEL
857 return set_ref_in_item_job(tv->vval.v_job, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100858 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200859#else
860 break;
861#endif
862
863 case VAR_CHANNEL:
864#ifdef FEAT_JOB_CHANNEL
865 return set_ref_in_item_channel(tv->vval.v_channel, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100866 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200867#else
868 break;
869#endif
870
871 case VAR_CLASS:
872 return set_ref_in_item_class(tv->vval.v_class, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100873 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200874
875 case VAR_OBJECT:
876 return set_ref_in_item_object(tv->vval.v_object, copyID,
Yegappan Lakshmanan9cb865e2025-03-23 16:42:16 +0100877 ht_stack, list_stack, tuple_stack);
Yegappan Lakshmanan25536f42024-05-22 16:45:04 +0200878
879 case VAR_UNKNOWN:
880 case VAR_ANY:
881 case VAR_VOID:
882 case VAR_BOOL:
883 case VAR_SPECIAL:
884 case VAR_NUMBER:
885 case VAR_FLOAT:
886 case VAR_STRING:
887 case VAR_BLOB:
888 case VAR_TYPEALIAS:
889 case VAR_INSTR:
890 // Types that do not contain any other item
891 break;
892 }
893
894 return abort;
895}
896
897#endif