summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog7
-rw-r--r--generic/tclProc.c29
-rw-r--r--tests/proc.test36
3 files changed, 62 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog
index 023241f..030f11c 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+2008-08-11 Andreas Kupries <andreask@activestate.com>
+
+ * generic/tclProc.c (Tcl_ProcObjCmd): Fixed memory leak triggered
+ * tests/proc.test: by procbody::test::proc. See [Bug 2043636].
+ Added a test case demonstrating the leak before the fix. Fixed a
+ few spelling errors in test descriptions as well.
+
2008-07-28 Andreas Kupries <andreask@activestate.com>
* generic/tclBasic.c: Added missing release of extended command
diff --git a/generic/tclProc.c b/generic/tclProc.c
index b2c2b8d..950d448 100644
--- a/generic/tclProc.c
+++ b/generic/tclProc.c
@@ -11,7 +11,7 @@
* See the file "license.terms" for information on usage and redistribution
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
*
- * RCS: @(#) $Id: tclProc.c,v 1.44.2.8 2008/07/21 19:37:45 andreas_kupries Exp $
+ * RCS: @(#) $Id: tclProc.c,v 1.44.2.9 2008/08/11 20:13:43 andreas_kupries Exp $
*/
#include "tclInt.h"
@@ -182,7 +182,8 @@ Tcl_ProcObjCmd(dummy, interp, objc, objv)
context.line &&
(context.nline >= 4) &&
(context.line [3] >= 0)) {
- int new;
+ int isNew;
+ Tcl_HashEntry* hePtr;
CmdFrame* cfPtr = (CmdFrame*) ckalloc (sizeof (CmdFrame));
cfPtr->level = -1;
@@ -206,9 +207,27 @@ Tcl_ProcObjCmd(dummy, interp, objc, objv)
cfPtr->cmd.str.cmd = NULL;
cfPtr->cmd.str.len = 0;
- Tcl_SetHashValue (Tcl_CreateHashEntry (iPtr->linePBodyPtr,
- (char*) procPtr, &new),
- cfPtr);
+ hePtr = Tcl_CreateHashEntry (iPtr->linePBodyPtr, (char*) procPtr,
+ &isNew);
+ if (!isNew) {
+ /*
+ * Get the old command frame and release it. See also
+ * TclProcCleanupProc in this file. Currently it seems as if
+ * only the procbodytest::proc command of the testsuite is
+ * able to trigger this situation.
+ */
+
+ CmdFrame* cfOldPtr = (CmdFrame *) Tcl_GetHashValue(hePtr);
+
+ if (cfOldPtr->type == TCL_LOCATION_SOURCE) {
+ Tcl_DecrRefCount(cfOldPtr->data.eval.path);
+ cfOldPtr->data.eval.path = NULL;
+ }
+ ckfree((char *) cfOldPtr->line);
+ cfOldPtr->line = NULL;
+ ckfree((char *) cfOldPtr);
+ }
+ Tcl_SetHashValue (hePtr, cfPtr);
}
}
#endif
diff --git a/tests/proc.test b/tests/proc.test
index 222a31f..8a9ec14 100644
--- a/tests/proc.test
+++ b/tests/proc.test
@@ -13,13 +13,15 @@
# See the file "license.terms" for information on usage and redistribution
# of this file, and for a DISCLAIMER OF ALL WARRANTIES.
#
-# RCS: @(#) $Id: proc.test,v 1.11.2.1 2004/05/02 21:07:16 msofer Exp $
+# RCS: @(#) $Id: proc.test,v 1.11.2.2 2008/08/11 20:13:44 andreas_kupries Exp $
if {[lsearch [namespace children] ::tcltest] == -1} {
package require tcltest
namespace import -force ::tcltest::*
}
+testConstraint memory [llength [info commands memory]]
+
catch {eval namespace delete [namespace children :: test_ns_*]}
catch {rename p ""}
catch {rename {} ""}
@@ -237,7 +239,7 @@ test proc-4.3 {TclCreateProc, procbody obj, too many args} {
set result
} {procedure "t": arg list contains 3 entries, precompiled header expects 1}
-test proc-4.4 {TclCreateProc, procbody obj, inconsitent arg name} {
+test proc-4.4 {TclCreateProc, procbody obj, inconsistent arg name} {
catch {
proc p {x y z} {
set v [join [list $x $y $z]]
@@ -254,7 +256,7 @@ test proc-4.4 {TclCreateProc, procbody obj, inconsitent arg name} {
set result
} {procedure "t": formal parameter 1 is inconsistent with precompiled body}
-test proc-4.5 {TclCreateProc, procbody obj, inconsitent arg default type} {
+test proc-4.5 {TclCreateProc, procbody obj, inconsistent arg default type} {
catch {
proc p {x y {z Z}} {
set v [join [list $x $y $z]]
@@ -271,7 +273,7 @@ test proc-4.5 {TclCreateProc, procbody obj, inconsitent arg default type} {
set result
} {procedure "t": formal parameter 2 is inconsistent with precompiled body}
-test proc-4.6 {TclCreateProc, procbody obj, inconsitent arg default type} {
+test proc-4.6 {TclCreateProc, procbody obj, inconsistent arg default type} {
catch {
proc p {x y z} {
set v [join [list $x $y $z]]
@@ -288,7 +290,7 @@ test proc-4.6 {TclCreateProc, procbody obj, inconsitent arg default type} {
set result
} {procedure "t": formal parameter 2 is inconsistent with precompiled body}
-test proc-4.7 {TclCreateProc, procbody obj, inconsitent arg default value} {
+test proc-4.7 {TclCreateProc, procbody obj, inconsistent arg default value} {
catch {
proc p {x y {z Z}} {
set v [join [list $x $y $z]]
@@ -305,6 +307,30 @@ test proc-4.7 {TclCreateProc, procbody obj, inconsitent arg default value} {
set result
} {procedure "t": formal parameter "z" has default value inconsistent with precompiled body}
+test proc-4.8 {TclCreateProc, procbody obj, no leak on multiple iterations} -setup {
+ proc getbytes {} {
+ set lines [split [memory info] "\n"]
+ lindex $lines 3 3
+ }
+ proc px x {
+ set y [string tolower $x]
+ return "$x:$y"
+ }
+ px x
+} -constraints memory -body {
+ set end [getbytes]
+ for {set i 0} {$i < 5} {incr i} {
+
+ procbodytest::proc tx x px
+
+ set tmp $end
+ set end [getbytes]
+ }
+ set leakedBytes [expr {$end - $tmp}]
+} -cleanup {
+ rename getbytes {}
+} -result 0
+
test proc-5.1 {Bytecompiling noop; test for correct argument substitution} {
proc p args {} ; # this will be bytecompiled into t
proc t {} {