Itcl trace methods

Adding traces to a method of an object is slightly harder than in basic tcl. Here is an example showing the problem and my suggested cure.

The real problem is that a method of an object can be called in 2 different ways -

  • from anywhere prefixed with the object name (unprotected methods only)
  • from another method of the class, without the object name prefix.

The first method is not detected by a simple trace since the command is actually the object, not the method. The fact that the method is then called does not get detected by the trace (as shown below).

How do we add traces to every entry/exit from any method of an Itcl class? The simple answer is shown below to be incomplete since methods called from inside the object do not need $this prefixed to the command. In particular this means that traces on a method invoked from outside the object (eg as a response to a Tk widget) are not detected unless the fix below is applied.

A trace on each method is set below, then a fix to analyse calls prefixed with the object name is applied (see proc enterexit).

  package require Itcl
  itcl::class helloworld {
    method greet {} { puts "Hello World from [info level 0]" ; return "Ping"}
    method indirect args { eval $args }
  }
  helloworld  h1  ;# create a class instance (object) called h1
 
  # response to a trace
  proc enterexit args { set fn [lindex [lindex $args 0] 0]
         if {[itcl::is object $fn]} { ;# identify the method called.
                enterexit [lrange [lindex $args 0] 1 end] [lindex $args end]
        } else {puts "traceat [lindex $args end]: [namespace tail $fn]" }
  }

  proc testtraces {} {
 console show;  update idletasks
  # now add a trace to all the methods of helloworld
  set fnlist [h1 info function]
  foreach fn $fnlist { ;# add tracing onto each method
      puts "add trace to Function $fn [info command $fn]"
      trace add execution $fn {enter leave } "enterexit $fn"
  }
 
  puts "\n  NB this execution is not traced "
  h1 greet ;# 
  puts "\n  BUT this indirect execution IS traced"
  h1 indirect greet ;# does trace

   trace add execution h1 {enter leave } "enterexit"
   puts "\n  After adding a trace to h1, the execution is traced"
   puts "  by the recursive line in proc enterexit"
  h1 greet ;# 
    puts "\n  And the method \"indirect\" is also traced here:"
  h1 indirect greet ;# 
  }
  testtraces ;# to clarify output the test has been placed into a proc