sl@0: # This file contains tests for the files tclCompile.c, tclCompCmds.c sl@0: # and tclLiteral.c sl@0: # sl@0: # This file contains a collection of tests for one or more of the Tcl sl@0: # built-in commands. Sourcing this file into Tcl runs the tests and sl@0: # generates output for errors. No output means no errors were found. sl@0: # sl@0: # Copyright (c) 1997 by Sun Microsystems, Inc. sl@0: # Copyright (c) 1998-1999 by Scriptics Corporation. sl@0: # sl@0: # See the file "license.terms" for information on usage and redistribution sl@0: # of this file, and for a DISCLAIMER OF ALL WARRANTIES. sl@0: # sl@0: # RCS: @(#) $Id: compile.test,v 1.24.2.3 2004/10/26 20:14:36 dgp Exp $ sl@0: sl@0: package require tcltest 2 sl@0: namespace import -force ::tcltest::* sl@0: sl@0: # The following tests are very incomplete, although the rest of the sl@0: # test suite covers this file fairly well. sl@0: sl@0: catch {rename p ""} sl@0: catch {namespace delete test_ns_compile} sl@0: catch {unset x} sl@0: catch {unset y} sl@0: catch {unset a} sl@0: sl@0: test compile-1.1 {TclCompileString: look up cmds in proc ns, not current ns} { sl@0: catch {namespace delete test_ns_compile} sl@0: catch {unset x} sl@0: set x 123 sl@0: namespace eval test_ns_compile { sl@0: proc set {args} { sl@0: global x sl@0: lappend x test_ns_compile::set sl@0: } sl@0: proc p {} { sl@0: set 0 sl@0: } sl@0: } sl@0: list [test_ns_compile::p] [set x] sl@0: } {{123 test_ns_compile::set} {123 test_ns_compile::set}} sl@0: test compile-1.2 {TclCompileString, error result is reset if TclGetLong determines word isn't an integer} { sl@0: proc p {x} {info commands 3m} sl@0: list [catch {p} msg] $msg sl@0: } {1 {wrong # args: should be "p x"}} sl@0: test compile-2.1 {TclCompileDollarVar: global scalar name with ::s} { sl@0: catch {unset x} sl@0: set x 123 sl@0: list $::x [expr {[lsearch -exact [info globals] x] != 0}] sl@0: } {123 1} sl@0: test compile-2.2 {TclCompileDollarVar: global scalar name with ::s} { sl@0: catch {unset y} sl@0: proc p {} { sl@0: set ::y 789 sl@0: return $::y sl@0: } sl@0: list [p] $::y [expr {[lsearch -exact [info globals] y] != 0}] sl@0: } {789 789 1} sl@0: test compile-2.3 {TclCompileDollarVar: global array name with ::s} { sl@0: catch {unset a} sl@0: set ::a(1) 2 sl@0: list $::a(1) [set ::a($::a(1)) 3] $::a(2) [expr {[lsearch -exact [info globals] a] != 0}] sl@0: } {2 3 3 1} sl@0: test compile-2.4 {TclCompileDollarVar: global scalar name with ::s} { sl@0: catch {unset a} sl@0: proc p {} { sl@0: set ::a(1) 1 sl@0: return $::a($::a(1)) sl@0: } sl@0: list [p] $::a(1) [expr {[lsearch -exact [info globals] a] != 0}] sl@0: } {1 1 1} sl@0: test compile-2.5 {TclCompileDollarVar: global array, called as ${arrName(0)}} { sl@0: catch {unset a} sl@0: proc p {} { sl@0: global a sl@0: set a(1) 1 sl@0: return ${a(1)}$::a(1)$a(1) sl@0: } sl@0: list [p] $::a(1) [expr {[lsearch -exact [info globals] a] != 0}] sl@0: } {111 1 1} sl@0: sl@0: test compile-3.1 {TclCompileCatchCmd: only catch cmds with scalar vars are compiled inline} { sl@0: catch {unset a} sl@0: set a(1) xyzzyx sl@0: proc p {} { sl@0: global a sl@0: catch {set x 123} a(1) sl@0: } sl@0: list [p] $a(1) sl@0: } {0 123} sl@0: test compile-3.2 {TclCompileCatchCmd: non-local variables} { sl@0: set ::foo 1 sl@0: proc catch-test {} { sl@0: catch {set x 3} ::foo sl@0: } sl@0: catch-test sl@0: set ::foo sl@0: } 3 sl@0: test compile-3.3 {TclCompileCatchCmd: overagressive compiling [bug 219184]} { sl@0: proc catch-test {str} { sl@0: catch [eval $str GOOD] sl@0: error BAD sl@0: } sl@0: catch {catch-test error} ::foo sl@0: set ::foo sl@0: } {GOOD} sl@0: test compile-3.4 {TclCompileCatchCmd: bcc'ed [return] is caught} { sl@0: proc foo {} { sl@0: set fail [catch { sl@0: return 1 sl@0: }] ; # {} sl@0: return 2 sl@0: } sl@0: foo sl@0: } {2} sl@0: sl@0: test compile-3.5 {TclCompileCatchCmd: recover from error, [Bug 705406]} { sl@0: proc foo {} { sl@0: catch { sl@0: if {[a]} { sl@0: if b {} sl@0: } sl@0: } sl@0: } sl@0: list [catch foo msg] $msg sl@0: } {0 1} sl@0: sl@0: test compile-4.1 {TclCompileForCmd: command substituted test expression} { sl@0: set i 0 sl@0: set j 0 sl@0: # Should be "forever" sl@0: for {} [expr $i < 3] {} { sl@0: set j [incr i] sl@0: if {$j > 3} break sl@0: } sl@0: set j sl@0: } {4} sl@0: sl@0: test compile-5.1 {TclCompileForeachCmd: exception stack} { sl@0: proc foreach-exception-test {} { sl@0: foreach array(index) [list 1 2 3] break sl@0: foreach array(index) [list 1 2 3] break sl@0: foreach scalar [list 1 2 3] break sl@0: } sl@0: list [catch foreach-exception-test result] $result sl@0: } {0 {}} sl@0: test compile-5.2 {TclCompileForeachCmd: non-local variables} { sl@0: set ::foo 1 sl@0: proc foreach-test {} { sl@0: foreach ::foo {1 2 3} {} sl@0: } sl@0: foreach-test sl@0: set ::foo sl@0: } 3 sl@0: sl@0: test compile-6.1 {TclCompileSetCmd: global scalar names with ::s} { sl@0: catch {unset x} sl@0: catch {unset y} sl@0: set x 123 sl@0: proc p {} { sl@0: set ::y 789 sl@0: return $::y sl@0: } sl@0: list $::x [expr {[lsearch -exact [info globals] x] != 0}] \ sl@0: [p] $::y [expr {[lsearch -exact [info globals] y] != 0}] sl@0: } {123 1 789 789 1} sl@0: test compile-6.2 {TclCompileSetCmd: global array names with ::s} { sl@0: catch {unset a} sl@0: set ::a(1) 2 sl@0: proc p {} { sl@0: set ::a(1) 1 sl@0: return $::a($::a(1)) sl@0: } sl@0: list $::a(1) [p] [set ::a($::a(1)) 3] $::a(1) [expr {[lsearch -exact [info globals] a] != 0}] sl@0: } {2 1 3 3 1} sl@0: test compile-6.3 {TclCompileSetCmd: namespace var names with ::s} { sl@0: catch {namespace delete test_ns_compile} sl@0: catch {unset x} sl@0: namespace eval test_ns_compile { sl@0: variable v hello sl@0: variable arr sl@0: set ::x $::test_ns_compile::v sl@0: set ::test_ns_compile::arr(1) 123 sl@0: } sl@0: list $::x $::test_ns_compile::arr(1) sl@0: } {hello 123} sl@0: sl@0: test compile-7.1 {TclCompileWhileCmd: command substituted test expression} { sl@0: set i 0 sl@0: set j 0 sl@0: # Should be "forever" sl@0: while [expr $i < 3] { sl@0: set j [incr i] sl@0: if {$j > 3} break sl@0: } sl@0: set j sl@0: } {4} sl@0: sl@0: test compile-8.1 {CollectArgInfo: binary data} { sl@0: list [catch "string length \000foo" msg] $msg sl@0: } {0 4} sl@0: test compile-8.2 {CollectArgInfo: binary data} { sl@0: list [catch "string length foo\000" msg] $msg sl@0: } {0 4} sl@0: test compile-8.3 {CollectArgInfo: handle "]" at end of command properly} { sl@0: set x ] sl@0: } {]} sl@0: sl@0: test compile-9.1 {UpdateStringOfByteCode: called for duplicate of compiled empty object} { sl@0: proc p {} { sl@0: set x {} sl@0: eval $x sl@0: append x { } sl@0: eval $x sl@0: } sl@0: p sl@0: } {} sl@0: sl@0: test compile-10.1 {BLACKBOX: exception stack overflow} { sl@0: set x {{0}} sl@0: set y 0 sl@0: while {$y < 100} { sl@0: if !$x {incr y} sl@0: } sl@0: } {} sl@0: sl@0: test compile-11.1 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { sl@0: # shared object - Interp result && Var 'r' sl@0: set r [list foobar] sl@0: # command that will add error to result sl@0: lindex a bogus sl@0: } sl@0: list [catch {p} msg] $msg sl@0: } {1 {bad index "bogus": must be integer or end?-integer?}} sl@0: test compile-11.2 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; string index a bogus } sl@0: list [catch {p} msg] $msg sl@0: } {1 {bad index "bogus": must be integer or end?-integer?}} sl@0: test compile-11.3 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; string index a 09 } sl@0: list [catch {p} msg] $msg sl@0: } {1 {bad index "09": must be integer or end?-integer? (looks like invalid octal number)}} sl@0: test compile-11.4 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; array set var {one two many} } sl@0: list [catch {p} msg] $msg sl@0: } {1 {list must have an even number of elements}} sl@0: test compile-11.5 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; incr foo } sl@0: list [catch {p} msg] $msg sl@0: } {1 {can't read "foo": no such variable}} sl@0: test compile-11.6 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; incr foo bogus } sl@0: list [catch {p} msg] $msg sl@0: } {1 {expected integer but got "bogus"}} sl@0: test compile-11.7 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; expr !a } sl@0: list [catch {p} msg] $msg sl@0: } {1 {syntax error in expression "!a": variable references require preceding $}} sl@0: test compile-11.8 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; expr {!a} } sl@0: list [catch {p} msg] $msg sl@0: } {1 {syntax error in expression "!a": variable references require preceding $}} sl@0: test compile-11.9 {Tcl_Append*: ensure Tcl_ResetResult is used properly} { sl@0: proc p {} { set r [list foobar] ; llength "\{" } sl@0: list [catch {p} msg] $msg sl@0: } {1 {unmatched open brace in list}} sl@0: sl@0: # sl@0: # Special section for tests of tclLiteral.c sl@0: # The following tests check for incorrect memory handling in sl@0: # TclReleaseLiteral. They are only effective when tcl is compiled sl@0: # with TCL_MEM_DEBUG sl@0: # sl@0: # Special test for leak on interp delete [Bug 467523]. sl@0: ::tcltest::testConstraint exec [llength [info commands exec]] sl@0: ::tcltest::testConstraint memDebug [llength [info commands memory]] sl@0: sl@0: test compile-12.1 {testing literal leak on interp delete} {memDebug} { sl@0: proc getbytes {} { sl@0: set lines [split [memory info] "\n"] sl@0: lindex [lindex $lines 3] 3 sl@0: } sl@0: sl@0: set end [getbytes] sl@0: for {set i 0} {$i < 5} {incr i} { sl@0: interp create foo sl@0: foo eval { sl@0: namespace eval bar {} sl@0: } sl@0: interp delete foo sl@0: set tmp $end sl@0: set end [getbytes] sl@0: } sl@0: rename getbytes {} sl@0: set leak [expr {$end - $tmp}] sl@0: } 0 sl@0: # Special test for a memory error in a preliminary fix of [Bug 467523]. sl@0: # It requires executing a helpfile. Presumably the child process is sl@0: # used because when this test fails, it crashes. sl@0: test compile-12.2 {testing error on literal deletion} {memDebug exec} { sl@0: makeFile { sl@0: for {set i 0} {$i < 5} {incr i} { sl@0: namespace eval bar {} sl@0: namespace delete bar sl@0: } sl@0: puts 0 sl@0: } source.file sl@0: set res [catch { sl@0: exec [interpreter] source.file sl@0: }] sl@0: catch {removeFile source.file} sl@0: set res sl@0: } 0 sl@0: # Test to catch buffer overrun in TclCompileTokens from buf 530320 sl@0: test compile-12.3 {check for a buffer overrun} { sl@0: proc crash {} { sl@0: puts $array([expr {a+2}]) sl@0: } sl@0: list [catch crash msg] $msg sl@0: } {1 {syntax error in expression "a+2": variable references require preceding $}} sl@0: sl@0: test compile-12.4 {TclCleanupLiteralTable segfault} { sl@0: # Tcl Bug 1001997 sl@0: # Here, we're trying to test a case that causes a crash in sl@0: # TclCleanupLiteralTable. The conditions that we're trying to sl@0: # establish are: sl@0: # - TclCleanupLiteralTable is attempting to clean up a bytecode sl@0: # object in the literal table. sl@0: # - The bytecode object in question contains the only reference sl@0: # to another literal. sl@0: # - The literal in question is in the same hash bucket as the bytecode sl@0: # object, and immediately follows it in the chain. sl@0: # Since newly registered literals are added at the FRONT of the sl@0: # bucket chains, and since the bytecode object is registered before sl@0: # its literals, this is difficult to achieve. What we do is: sl@0: # (a) do a [namespace eval] of a string that's calculated to sl@0: # hash into the same bucket as a literal that it contains. sl@0: # In this case, the script and the variable 'bugbug' sl@0: # land in the same bucket. sl@0: # (b) do a [namespace eval] of a string that contains enough sl@0: # literals to force TclRegisterLiteral to rebuild the global sl@0: # literal table. The newly created hash buckets will contain sl@0: # the literals, IN REVERSE ORDER, thus putting the bytecode sl@0: # immediately ahead of 'bugbug' and 'bug4345bug'. The bytecode sl@0: # object will contain the only references to those two literals. sl@0: # (c) Delete the interpreter to invoke TclCleanupLiteralTable sl@0: # and tickle the bug. sl@0: proc foo {} { sl@0: set i [interp create] sl@0: $i eval { sl@0: namespace eval ::w {concat 4649; variable bugbug} sl@0: namespace eval ::w { sl@0: concat x1 x2 x3 x4 x5 x6 x7 x8 x9 x10 \ sl@0: x11 x12 x13 x14 x15 x16 x17 x18 x19 x20 \ sl@0: x21 x22 x23 x24 x25 x26 x27 x28 x29 x30 \ sl@0: x31 x32 X33 X34 X35 X36 X37 X38 X39 X40 \ sl@0: x41 x42 x43 x44 x45 x46 x47 x48 x49 x50 \ sl@0: x51 x52 x53 x54 x55 x56 x57 x58 x59 x60 \ sl@0: x61 x62 x63 x64 sl@0: concat y1 y2 y3 y4 y5 y6 y7 y8 y9 y10 \ sl@0: y11 y12 y13 y14 y15 y16 y17 y18 y19 y20 \ sl@0: y21 y22 y23 y24 y25 y26 y27 y28 y29 y30 \ sl@0: y31 y32 Y33 Y34 Y35 Y36 Y37 Y38 Y39 Y40 \ sl@0: y41 y42 y43 y44 y45 y46 y47 y48 y49 y50 \ sl@0: y51 y52 y53 y54 y55 y56 y57 y58 y59 y60 \ sl@0: y61 y62 y63 y64 sl@0: concat z1 z2 z3 z4 z5 z6 z7 z8 z9 z10 \ sl@0: z11 z12 z13 z14 z15 z16 z17 z18 z19 z20 \ sl@0: z21 z22 z23 z24 z25 z26 z27 z28 z29 z30 \ sl@0: z31 z32 sl@0: } sl@0: } sl@0: interp delete $i; # must not crash sl@0: return ok sl@0: } sl@0: foo sl@0: } ok sl@0: sl@0: sl@0: # Special test for underestimating the maxStackSize required for a sl@0: # compiled command. A failure will cause a segfault in the child sl@0: # process. sl@0: test compile-13.1 {testing underestimate of maxStackSize in list cmd} {exec} { sl@0: set body {set x [list} sl@0: for {set i 0} {$i < 3000} {incr i} { sl@0: append body " $i" sl@0: } sl@0: append body {]; puts OK} sl@0: regsub BODY {proc crash {} {BODY}; crash} $body script sl@0: list [catch {exec [interpreter] << $script} msg] $msg sl@0: } {0 OK} sl@0: sl@0: # Special test for compiling tokens from a copy of the source sl@0: # string [Bug #599788] sl@0: test compile-14.1 {testing errors in element name; segfault?} {} { sl@0: catch {set a([error])} msg1 sl@0: catch {set bubba([join $abba $jubba]) $vol} msg2 sl@0: list $msg1 $msg2 sl@0: } {{wrong # args: should be "error message ?errorInfo? ?errorCode?"} {can't read "abba": no such variable}} sl@0: sl@0: # Next 4 tests cover Tcl Bug 633204 sl@0: test compile-15.1 {proper TCL_RETURN code from [return]} { sl@0: proc p {} {catch return} sl@0: set result [p] sl@0: rename p {} sl@0: set result sl@0: } 2 sl@0: test compile-15.2 {proper TCL_RETURN code from [return]} { sl@0: proc p {} {catch {return foo}} sl@0: set result [p] sl@0: rename p {} sl@0: set result sl@0: } 2 sl@0: test compile-15.3 {proper TCL_RETURN code from [return]} { sl@0: proc p {} {catch {return $::tcl_library}} sl@0: set result [p] sl@0: rename p {} sl@0: set result sl@0: } 2 sl@0: test compile-15.4 {proper TCL_RETURN code from [return]} { sl@0: proc p {} {catch {return [info library]}} sl@0: set result [p] sl@0: rename p {} sl@0: set result sl@0: } 2 sl@0: test compile-15.5 {proper TCL_RETURN code from [return]} { sl@0: proc p {} {catch {set a 1}; return} sl@0: set result [p] sl@0: rename p {} sl@0: set result sl@0: } "" sl@0: sl@0: sl@0: # cleanup sl@0: catch {rename p ""} sl@0: catch {namespace delete test_ns_compile} sl@0: catch {unset x} sl@0: catch {unset y} sl@0: catch {unset a} sl@0: ::tcltest::cleanupTests sl@0: return