Index: Makefile.in =================================================================== diff -u -r8d4f0d69f9586bdafbffa45b0368b84b86169bca -r2fcc2f0db81ba75af31e0578ca240be8fbb0a801 --- Makefile.in (.../Makefile.in) (revision 8d4f0d69f9586bdafbffa45b0368b84b86169bca) +++ Makefile.in (.../Makefile.in) (revision 2fcc2f0db81ba75af31e0578ca240be8fbb0a801) @@ -344,6 +344,8 @@ test-core: $(TCLSH_PROG) $(TCLSH) $(src_test_dir_native)/object-system.xotcl \ -libdir $(PLATFORM_DIR) $(TESTFLAGS) + $(TCLSH) $(src_test_dir_native)/destroytest.xotcl \ + -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/testx.xotcl \ -libdir $(PLATFORM_DIR) $(TESTFLAGS) $(TCLSH) $(src_test_dir_native)/testo.xotcl \ Index: tests/destroytest.xotcl =================================================================== diff -u --- tests/destroytest.xotcl (revision 0) +++ tests/destroytest.xotcl (revision 2fcc2f0db81ba75af31e0578ca240be8fbb0a801) @@ -0,0 +1,428 @@ +package require XOTcl +namespace import -force xotcl::* +package require xotcl::test + +proc ? {cmd expected {msg ""}} { + set count 10 + if {$msg ne ""} { + set t [Test new -cmd $cmd -count $count -msg $msg] + } else { + set t [Test new -cmd $cmd -count $count] + } + $t expected $expected + $t run +} +Class O -superclass Object +O instproc init {} { + set ::ObjectDestroy 0 + set ::firstDestroy 0 +} +O instproc destroy {} { + incr ::ObjectDestroy + #[my info class] dealloc [self] + next +} + +# +# classical simple case +# +set case "simple destroy (1)" +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + my destroy + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + ? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBBB" + ? {Object isobject c1} 1 "$::case object still exists in proc" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 0 "$::case object deleted" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" + +# +# simple case, destroy does not propagate, c1 survives +# +set case "simple destroy (2), destroy blocks" +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy block"} +C instproc foo {} { + puts stderr "==== $::case [self]" + my destroy + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + ? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBBB" + ? {Object isobject c1} 1 "$::case object still exists in proc" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 1 "$::case object deleted" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + +# +# simple object recreate +# +set case "recreate" +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + [my info class] create [self] + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + ? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBBB" + ? {Object isobject c1} 1 "$::case object still exists in proc" + ? "set ::firstDestroy" 0 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 1 "$::case object deleted" +? "set ::firstDestroy" 0 "$::case, firstDestroy called" + +# +# cmd rename to empty, xotcl provides its own rename and calls destroy +# .. like simple case above +# +set case "cmd rename empty (1)" +Object o +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + rename [self] "" + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + ? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + ? {Object isobject c1} 1 "$::case object still exists in proc" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 0 "$::case object still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" + +# +# cmd rename to empty, xotcl provides its own rename and calls +# destroy, but destroy does not propagate, c1 survives rename, since +# this is the situation like above, as long xotcl's rename is used. +# +set case "cmd rename empty (2)" +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy block"} +C instproc foo {} { + puts stderr "==== $::case [self]" + rename [self] "" + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + ? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + ? {Object isobject c1} 1 "$::case object still exists in proc" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +puts stderr ======[c1 set x] +? {Object isobject c1} 1 "$::case object still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + +# +# cmd rename other xotcl object to current, +# xotcl's rename invokes a move +# +set case "cmd rename object to self" +Object o +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + rename o [self] + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + ? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + ? {Object isobject c1} 1 "$::case object still exists in proc" + ? "set ::firstDestroy" 0 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 1 "$::case object still exists after proc" +? "set ::firstDestroy" 0 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + +# +# cmd rename other proc to current object, +# xotcl's rename invokes a move +# +set case "cmd rename proc to self" +proc o args {} +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + set x [catch {rename o [self]}] + ? "set _ $x" 1 "$::case tcl refuses to rename into an existing command" +} +C c1 +c1 foo +? {Object isobject c1} 1 "$::case object still exists after proc" +? "set ::firstDestroy" 0 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + + +# +# namespace delete: tcl delays delete until the namespace is not +# active anymore. destroy is called after BBBB. Hypothesis: destroy is +# called only when we are lucky, since C might be destroyed before c1 +# by the namespace delete +# + +set case "delete parent namespace (1)" +namespace eval ::test { + Class C -superclass O + C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} + C instproc foo {} { + puts stderr "==== $::case [self]" + namespace delete ::test + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + # + # If the following line is commented in, the namespace is deleted + # here. Is there a bug with nsPtr->activationCount + # + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + puts stderr "???? [self] exists [Object isobject [self]]" + ? "Object isobject [self]" 0 ;# WHY? + puts stderr "???? [self] exists [Object isobject [self]]" + ? "set ::firstDestroy" 0 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case destroy not yet called" + } +} +test::C test::c1 +test::c1 foo +puts stderr ======[Object isobject test::c1] +? {Object isobject test::c1} 0 "$::case object still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 1 "$::case: destroy was called when poping stack frame" + +# +# namespace delete: tcl delays delete until the namespace is not +# active anymore. destroy is called after BBBB, but does not +# propagate. +# +set case "delete parent namespace (2)" +namespace eval ::test { + Class C -superclass O + C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy block"} + C instproc foo {} { + puts stderr "==== $::case [self]" + namespace delete ::test + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + # + # If the following line is commented in, the namespace is deleted + # here. Is there a bug with nsPtr->activationCount + # + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + puts stderr "???? [self] exists [Object isobject [self]]" + ? "Object isobject [self]" 0 "$::case object still exists in proc";# WHY? + puts stderr "???? [self] exists [Object isobject [self]]" + ? "set ::firstDestroy" 0 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called"; # NOT YET CALLED + } +} +test::C test::c1 +test::c1 foo +puts stderr ======[Object isobject test::c1] +? {Object isobject test::c1} 0 "$::case object still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" ;# toplevel destroy was blocked + +# +# controlled namespace delete: xotcl has its own namespace cleanup, +# topological order should be always ok. however, the object o::c1 is +# already deleted, while a method of it is excuted +# +set case "delete parent object (1)" +Object o +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + o destroy + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + ? {Object isobject ::o::c1} 0 "$::case object still exists in proc" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" +} +C o::c1 +o::c1 foo +puts stderr ======[Object isobject ::o::c1] +? {Object isobject ::o::c1} 0 "$::case object o::c1 still exists after proc" +? {Object isobject o} 0 "$::case object o still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" + +# +# controlled namespace delete: xotcl has its own namespace cleanup. +# destroy does not delegate, but still o::c1 does not survive, since o +# is deleted. +# +set case "delete parent object (2)" +Object o +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy block"} +C instproc foo {} { + puts stderr "==== $::case [self]" + o destroy + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + ? {Object isobject ::o::c1} 0 "$::case object still exists in proc" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" +} +C o::c1 +o::c1 foo +puts stderr ======[Object isobject ::o::c1] +? {Object isobject ::o::c1} 0 "$::case object still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + + +# +# create an other cmd with the current object's name. +# xotcl 1.6 crashed on this test +# +set case "redefined current object as proc" +Object o +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + proc [self] {args} {puts HELLO} + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" + ? {Object isobject c1} 0 "$::case object still exists in proc" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 0 "$::case object still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" + + + +# +# delete the active class +# +set case "delete active class" +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + C destroy + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + #? [my info class] ::xotcl::Object "$::case, object reclassed" + ? [my info class] ::C "$::case, object reclassed?" + ? "set ::firstDestroy" 0 "$::case, firstDestroy called" + ? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + ? {Object isobject c1} 1 "$::case: object still exists in proc" + #? {Object isclass ::C} 0 "$::case: class still exists in proc" + ? {Object isclass ::C} 1 "$::case: class still exists in proc" +} +C c1 +c1 foo +puts stderr ======[Object isobject c1] +? {Object isobject c1} 1 "$::case: object still exists after proc" +? [c1 info class] ::xotcl::Object "$::case, after proc: object reclassed?" +? "set ::firstDestroy" 0 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + +# +# delete active object nested in class +# +set case "delete active object nested in class" +Class C -superclass O +C instproc destroy {} {incr ::firstDestroy; puts stderr " *** [self] destroy"; next} +C instproc foo {} { + puts stderr "==== $::case [self]" + C destroy + puts stderr "AAAA [self] exists [Object isobject [self]]" + my set x 1 + #? "[self] set x" 1 "$::case can still access [self]" + puts stderr "BBB" + #? "set ::firstDestroy" 0 "$::case, firstDestroy called" + ? "set ::firstDestroy" 1 "$::case, firstDestroy called" + #? "set ::ObjectDestroy" 0 "$::case, ObjectDestroy called" + ? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" + ? [my info class] ::C "$::case, object reclassed" + #? [my info class] ::xotcl::Object "$::case, object reclassed" + ? {Object isobject ::C::c1} 1 "$::case: object still exists in proc" + ? {Object isclass ::C} 1 "$::case: class still exists in proc" +} +C create ::C::c1 +C::c1 foo +puts stderr ======[Object isobject ::C::c1] +? {Object isobject ::C::c1} 0 "$::case: object still exists after proc" +? {Object isclass ::C} 0 "$::case: class still exists after proc" +? "set ::firstDestroy" 1 "$::case, firstDestroy called" +? "set ::ObjectDestroy" 1 "$::case, ObjectDestroy called" + +puts stderr "==== EXIT ====" +exit + +TODO: +fix crashes in regression test: DONE, + -> well we can't call traceprocs on the object being destroyed; maybe call CleanupDestroyObject() ealier + move destroy logic to activationCount DONE + simplify logic (remove callIsDestroy, callstate XOTCL_CSC_CALL_IS_DESTROY, destroyedCmd on stack content) DONE + remove CallStackMarkDestroyed(), CallStackMarkUndestroyed() DONE + remove traces of rst->callIsDestroy DONE + revive tclStack (without 85) DONE + check state changes DONE + +more generic */ +XOTCLINLINE static Tcl_ObjType * +GetCmdNameType(Tcl_ObjType *cmdType) { + + delete active class; maybe C destroy, c1 destroy (or C::c1 + C destroy) + add recreate logic test case + + MATRIX \ No newline at end of file