info body

info body procname

Returns the body of procname, which must be the name of a procedure.

Temporary Tcl_Obj

AMG: In Tcl 8.6.9, rather than return the same Tcl_Obj all the time, [info body] returns a new Tcl_Obj each time it is called. This is very surprising to me.

% tcl::unsupported::representation [info body tclPkgSetup]
value is a pure string with a refcount of 1, object pointer at 0x11c7630, string representation "
    global a..."
% tcl::unsupported::representation [info body tclPkgSetup]
value is a pure string with a refcount of 1, object pointer at 0x11c76c0, string representation "
    global a..."
% tcl::unsupported::representation [info body tclPkgSetup]
value is a pure string with a refcount of 1, object pointer at 0x11c7540, string representation "
    global a..."

Notice the object pointer keeps changing.

For argparse I am interested in caching the preparsed argument definition list in the script body Tcl_Obj, which I should be able to do without violating EIAS because (1) I will leave the string representation untouched, and (2) the argument definition list is derived solely from the script body and can be fully regenerated from it at any time, so shimmering is no obstacle.

Here is the relevant Tcl source code, which includes an explanation for this behavior: [L1 ].

Getting the current proc body

AMG: Getting the current proc body has a few surprising caveats. The first challenge is knowing the name of the current proc so that [info body] can be called. [lindex [info level 0] 0] is not quite enough because namespace eval or namespace ensemble may prevent the namespace name from showing up in the return value of [info level]. The solution is to use namespace origin:

namespace origin [lindex [info level 0] 0]

This gets even more interesting when trying to query the caller context, since [namespace origin] is sensitive to the current stack frame.

uplevel 1 [list namespace origin [lindex [info level -1] 0]]

The next challenge is dealing with apply lambdas. The above code will simply evaluate to apply when used within a lambda, then [info body apply] will complain that apply is not a procedure. To get the lambda body requires looking at [lindex [info level 0] 1 1]. But when to do this? I'm not 100% comfortable hard-coding the name apply, since commands can be renamed.

Instead I suggest using info frame, the return value of which is a dict that may include a lambda key.

[info frame] makes the first problem easier too. If its return value doesn't contain a lambda key, it may instead contain a proc key which helpfully stores the full command name, namespace qualifiers and all.

Pulling it all together, we get:

proc currentBody {} {
    set frame [info frame -1]
    if {[dict exists $frame lambda]} {
        lindex [dict get $frame lambda] 1
    } elseif {[dict exists $frame proc]} {
        info body [dict get $frame proc]
    } else {
        return -code error "not called from a procedure"
    }
}

Namespace importing

jkock 2006-09-22: If the proc has been imported from another namespace, then it is the body of that proc that is returned. This can lead to considerable confusion in relation with variable names, allowing a situation where a proc defined with the body returned from info body P will not behave in the same way as P itself!

Consider this example:

namespace eval x {
    variable q 123
    proc ww {} {
        variable q
        return $q
    }
    namespace export ww
}

namespace eval i {
    variable q 456
    namespace import -force ::x::ww
}

Now let us test the proc i::ww and see if we can understand it:

% i::ww
123
% info body i::ww

    variable q
    return $q

% set i::q
456

Quite mysterious, if we didn't know that i::ww was defined by namespace import.

I don't know if this behaviour should be considered a bug, you just have to be careful, and always do namespace origin before doing info body...


wdb: If you import a proc, it always "remembers" where it comes from. You can test it with namespace origin:

% namespace origin ::i::ww
::x::ww

The imported proc ::i::ww behaves as a link to ::x::ww.

But, if you rename ::x::ww to ::i::ww, then it really returns the value of ::i::q. So, since its behaviour is understood, it isn't strange any more, is it?


jkock 2006-09-22: in reply to wdb>Thanks for explaining what I already indicated in the last sentence.

I don't understand what you want to say with the rename example. What it is meant to explain or justify?

wdb: It was not my intent to annoy you. -- My example does not explain but illustrate how the proc behaves: if it is renamed to ::i::ww, then it behaves as if it were originally defined in namespace ::i. Obviously, it is always aware of its name.

For an explanation of the behaviour, I'd to look at the C sources. But, I am not willed to learn C. (That's one reason of my decision for Tcl).


jkock: OK, thanks. Within a few days I'll replace our discussion with a summary that sounds like it was written like that in the first place. (I agree with your viewpoint about C --- long live Tcl!) Cheers.

See also

info
Playing newLISP
stooopid info body tricks.