Posted to tcl by mr_calvin at Fri Oct 21 13:16:52 GMT 2016view raw
- # How-to run: `tclsh mem.tcl`
- #
- # Explanation: On shell exit, ::tcl namespace is not cleaned up, which
- # leaves Tcl_Objs referenced from within ::tcl with unbalanced
- # refcounts on exit. Their freeIntRepProcs are not executed. This
- # happens in interactive shells with the history (::history) feature
- # on, because it maintains the array ::tcl::history which, upon the
- # event Tcl_Objs shimmering from string to bytecode intreps, maintains
- # references beyond the point of shell exit (can be "fixed" by
- # deactivating the history by redefining the proc history to a NOOP,
- # or by calling ::history clear before exit).
- #
- # Example: The below example reproduces a variant of the issue in a
- # non-interactive shell (it is not a bytecode Tcl_Obj becoming
- # referenced from the ::tcl::HISTORY array, but a list). The refcount
- # bump in <<4b>> is not balanced (3->2) ... so, the freeIntRepProc of
- # 'new' Tcl_Obj is *never* (MethodFreeInternalRep) executed.
- set name "new"
- puts <<1>>[tcl::unsupported::representation $name]; # string(2)
- package req nx;
- puts <<2>>[tcl::unsupported::representation $name]; # string(2)
- set script [list ::nx::Object $name]
- puts <<3a>>[tcl::unsupported::representation $name]; # string(3) (+1 via list)
- puts <<3b>>[tcl::unsupported::representation $script]; # list(2)
- set ::tcl::HISTORY(1) $script
- ## FIX-1: the below would keep refcounts balanced on exit, because the global ns is cleared:
- # set ::tcl_HISTORY(1) $script
- puts <<4a>>[tcl::unsupported::representation $name]; # string(3)
- puts <<4b>>[tcl::unsupported::representation $script]; # list(3) (+1 via array)
- try $script; # $script is compiled (intrep -> bytecode), 'new' becomes nsfInstanceMethod intrep with NsfMethodContext
- puts <<5a>>[tcl::unsupported::representation $name]; # nsfInstanceMethod(3)
- puts <<5b>>[tcl::unsupported::representation $script]; # list(3)
- ## FIX-2: explicit deletes
- # unset ::tcl::HISTORY