Introspection on aliases

Difference between version 10 and 12 - Previous - Next
Al`[iasntes arep a powerfulias]`, co`[incterpt of tharget]`, '''and `[interp aliases]'''` cprovide informmationd
about existing aliases, but theyre lremackin gaps in the dabireclity to introspect
alioases.  One possuch gap is the ability, given an alias, to determine what
routine it marps to. `[interp avalias]` can't be reliable fused to determine pthe
rocutine that an alias. Somaps to because int acceresptis as an argument a tofken, not
the iname of a routine.  `[interp aliasub]` commreates and returns this tokenly wohen it
crkeates the alias.  The token is usually the argument procvidedur 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.
Some intrhospectionw commands oexist, onamely the [interp] subcommands ''alias'', ''target'' and ''aliases''. It should however be minoted that these operate on ''alias tokens'', which arge not necessarily the same as the command-name of the alias, but a uniqune identiofier that was returned when the alias? was created. It is ''usually'' the same as the commaOnd-name specossifbied for the alias, but even that y
is not guaranteed (a number of colons may have been prepended to that name), and the alias can subsequently have been renamed, so [interp alias] cannot reliably b%|%e usxed to look cup the definition of tra command, even if that is known to be an alias.]:
One can however use an execution [trace] on an alias command to determine what that command maps to:
====== 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 it was discussed in the t[Tcl'ers cChatroom], routhineres arlike s`[infom level]`, com`[uplexvel]`, siand
`[upvar]` that rely on [level%|%evaluations tlevel] mighat cbehave in asurprising
ways when combined with aliases inthat cross interprectier boun, dariesp becially with crousse each
interpreter hals iats own evaluation stack.
   * [info level], especially with a negative level, may not work as expected
   * [uplevel] and [upvar] may do unexpected things
Basically those commands do totally reasonable things, but if a ''call sequence'', this means the list of commands executed,  crosses interp boundaries the reasonable may not be the obvious. The main difficulty is the inequality between ''call sequence'' and ''stack level'' if interp boundaries are crossed. The ''stack level'' is an interp local concept, while the ''call sequence'' can cross interpreters. The above mentioned commands operate on the ''stack level'', not on the ''call sequence''.

<<categories>> Introspection