Posted to tcl by mr_calvin at Fri Oct 21 13:16:52 GMT 2016view pretty

# 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