comparison src/vim9execute.c @ 22541:7d6ba4204f66 v8.2.1819

patch 8.2.1819: Vim9: Memory leak when using a closure Commit: https://github.com/vim/vim/commit/85d5e2b723e6fc233e53252dd5c523944146fbc2 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Oct 10 14:13:01 2020 +0200 patch 8.2.1819: Vim9: Memory leak when using a closure Problem: Vim9: Memory leak when using a closure. Solution: Compute the mininal refcount in the funcstack. Reenable disabled tests.
author Bram Moolenaar <Bram@vim.org>
date Sat, 10 Oct 2020 14:15:04 +0200
parents c3e3c5707fe9
children 107eae953b87
comparison
equal deleted inserted replaced
22540:f0fd5cb81663 22541:7d6ba4204f66
347 347
348 // A closure is using the arguments and/or local variables. 348 // A closure is using the arguments and/or local variables.
349 // Move them to the called function. 349 // Move them to the called function.
350 if (funcstack == NULL) 350 if (funcstack == NULL)
351 return FAIL; 351 return FAIL;
352 funcstack->fs_ga.ga_len = argcount + STACK_FRAME_SIZE 352 funcstack->fs_var_offset = argcount + STACK_FRAME_SIZE;
353 + dfunc->df_varcount; 353 funcstack->fs_ga.ga_len = funcstack->fs_var_offset + dfunc->df_varcount;
354 stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len); 354 stack = ALLOC_CLEAR_MULT(typval_T, funcstack->fs_ga.ga_len);
355 funcstack->fs_ga.ga_data = stack; 355 funcstack->fs_ga.ga_data = stack;
356 if (stack == NULL) 356 if (stack == NULL)
357 { 357 {
358 vim_free(funcstack); 358 vim_free(funcstack);
374 // Move the local variables. 374 // Move the local variables.
375 for (idx = 0; idx < dfunc->df_varcount; ++idx) 375 for (idx = 0; idx < dfunc->df_varcount; ++idx)
376 { 376 {
377 tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE + idx); 377 tv = STACK_TV(ectx->ec_frame_idx + STACK_FRAME_SIZE + idx);
378 378
379 // Do not copy a partial created for a local function. 379 // A partial created for a local function, that is also used as a
380 // TODO: This won't work if the closure actually uses it. But when 380 // local variable, has a reference count for the variable, thus
381 // keeping it it gets complicated: it will create a reference cycle 381 // will never go down to zero. When all these refcounts are one
382 // inside the partial, thus needs special handling for garbage 382 // then the funcstack is unused. We need to count how many we have
383 // collection. 383 // so we need when to check.
384 // For now, decide on the reference count.
385 if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL) 384 if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL)
386 { 385 {
387 int i; 386 int i;
388 387
389 for (i = 0; i < closure_count; ++i) 388 for (i = 0; i < closure_count; ++i)
390 { 389 if (tv->vval.v_partial == ((partial_T **)gap->ga_data)[
391 partial_T *pt = ((partial_T **)gap->ga_data)[gap->ga_len 390 gap->ga_len - closure_count + i])
392 - closure_count + i]; 391 ++funcstack->fs_min_refcount;
393
394 if (tv->vval.v_partial == pt && pt->pt_refcount < 2)
395 break;
396 }
397 if (i < closure_count)
398 continue;
399 } 392 }
400 393
401 *(stack + argcount + STACK_FRAME_SIZE + idx) = *tv; 394 *(stack + funcstack->fs_var_offset + idx) = *tv;
402 tv->v_type = VAR_UNKNOWN; 395 tv->v_type = VAR_UNKNOWN;
403 } 396 }
404 397
405 for (idx = 0; idx < closure_count; ++idx) 398 for (idx = 0; idx < closure_count; ++idx)
406 { 399 {
422 gap->ga_len -= closure_count; 415 gap->ga_len -= closure_count;
423 if (gap->ga_len == 0) 416 if (gap->ga_len == 0)
424 ga_clear(gap); 417 ga_clear(gap);
425 418
426 return OK; 419 return OK;
420 }
421
422 /*
423 * Called when a partial is freed or its reference count goes down to one. The
424 * funcstack may be the only reference to the partials in the local variables.
425 * Go over all of them, the funcref and can be freed if all partials
426 * referencing the funcstack have a reference count of one.
427 */
428 void
429 funcstack_check_refcount(funcstack_T *funcstack)
430 {
431 int i;
432 garray_T *gap = &funcstack->fs_ga;
433 int done = 0;
434
435 if (funcstack->fs_refcount > funcstack->fs_min_refcount)
436 return;
437 for (i = funcstack->fs_var_offset; i < gap->ga_len; ++i)
438 {
439 typval_T *tv = ((typval_T *)gap->ga_data) + i;
440
441 if (tv->v_type == VAR_PARTIAL && tv->vval.v_partial != NULL
442 && tv->vval.v_partial->pt_funcstack == funcstack
443 && tv->vval.v_partial->pt_refcount == 1)
444 ++done;
445 }
446 if (done == funcstack->fs_min_refcount)
447 {
448 typval_T *stack = gap->ga_data;
449
450 // All partials referencing the funcstack have a reference count of
451 // one, thus the funcstack is no longer of use.
452 for (i = 0; i < gap->ga_len; ++i)
453 clear_tv(stack + i);
454 vim_free(stack);
455 vim_free(funcstack);
456 }
427 } 457 }
428 458
429 /* 459 /*
430 * Return from the current function. 460 * Return from the current function.
431 */ 461 */