Introspection on aliases

Difference between version 11 and 12 - Previous - Next
`[interp alias]`, `[interp target]`, and `[interp aliases]` provide information
about existing aliases, but there remain gaps in the ability to introspect
aliases.  One such gap is the ability, given an alias, to determine what
routine it maps to. `[interp alias]` can't be reliable used to determine the
routine that an alias maps to because it accepts as an argument a token, not
the name of a routine.  `[interp alias]` creates and returns this token when it
creates the alias.  The token is usually the argument provided as the alias
namei, but the name is not normalized, and if the alias may also be
subsequently renamed, in which case the token remains the same.
So how does one one determine the target routine of an alias?  One possibilitye
is to use [trace%|%execution trace]:

======
proc which_alias cmd {
    uplevel 1 [list ::trace add execution $cmd enterstep ::error]
    catch {uplevel 1 $cmd} res
    uplevel 1 [list ::trace remove execution $cmd enterstep ::error]
    return $res
}
======


----

Below some procs to inspect aliases simliar to '''[info] args''', a similar proc for '''info body''' can be built easily (just use the alias following outlined below and apply '''info body''' to the actual target of the alias).
'''Note:''' ''Since this procedure was written with the (incorrect) assumption that the [interp] subcommands will accept any variant of a command name as alias token, it doesn't always work.''

======
# [info args] like proc following an alias recursivly until it reaches
# the proc it originates from or cannot determine it.
# accounts for default parameters set by interp alias
#
proc aliasargs cmd {
    set orig $cmd

    set defaultargs [list]

    # loop until error or return occurs
    while 1 {
        # is it a proc already?
        if {[string equal [info procs $cmd] $cmd]} {
            set result [info args $cmd]
            # strip off the interp set default args
            return [lrange $result [llength $defaultargs] end]
        }
        # is it a built in or extension command we can get no args for?
        if {![string equal [info commands $cmd] $cmd]} {
            error "\"$orig\" isn't a procedure"
        }

        # catch bogus cmd names
        if {[lsearch [interp aliases {}] $cmd]==-1} {
            error "\"$orig\" isn't a procedure or alias or command"
        }

        if {[llength [set cmdargs [interp alias {} $cmd]]]>0} {
            # check if it is aliased in from another interpreter
            if {[catch {interp target {} $cmd} msg]} {
                error "Cannot resolve \"$orig\", alias leads to another interpreter."
            }
            if {$msg != {} } {
                error "Not recursing into slave interpreter \"$msg\".\
                       \"$orig\" could not be resolved."
            }
            # check if defaults are set for the alias
            if {[llength $cmdargs]>1} {
                set cmd [lindex $cmdargs 0]
                set defaultargs [concat [lrange $cmdargs 1 end] $defaultargs]
            } else {
                set cmd $cmdargs
            }
        }
    }
}
======

----

As discussed in [Tcl Chatroom], routines like `[info level]`, `[uplevel]`, and
`[upvar]` that rely on [level%|%evaluation level] might behave in surprising
ways when combined with aliases that cross interpreter boundaries because each
interpreter has its own evaluation stack.



<<categories>> Introspection