Mercurial > vim
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 */ |