如何找到被调用proc所在的脚本位置?

时间:2014-02-24 12:27:29

标签: tcl

该脚本已经提供了N个文件..,

source file 1
source file 2
.
.
source file N

当特定程序A调用。时,它实际存在于大多数源文件中。无论如何,包含该proc A的最后一个源文件将执行该函数。 当我调用proc时,如何找到包含proc的文件? 我可以使用任何代码来实现它吗?

1 个答案:

答案 0 :(得分:1)

最简单的方法(假设Tcl 8.5或8.6)是使用执行跟踪调用info frame来获取调用堆栈的详细信息。

trace add execution A enter callingA
proc callingA args {
    set ctxt [info frame -1]
    if {[dict exists $ctxt file] && [dict exists $ctxt proc]} {
        puts "Called [lindex $args 0 0] from [dict get $ctxt proc] in [dict get $ctxt file]"
    } elseif {[dict exists $ctxt proc]} {
        puts "Called [lindex $args 0 0] from [dict get $ctxt proc] (unknown location)"
    } else {
        # Fallback
        puts "Called [lindex $args 0 0] from within [file normalize [info script]]"
    }
}

info frame返回的词典中还有很多其他信息。


对于Tcl 8.4

在Tcl 8.4中,您没有info frame,Tcl也不记得默认情况下定义了哪些程序。你仍然有执行痕迹(它们是Tcl 8.4的新功能),这样就可以了。 (对于info script,我们必须要小心谨慎,因为 source期间只有{em>有效,而不是在完成之后;过程往往会在稍后调用。)

要获得定义每个过程的位置,您必须拦截proc本身,并在脚本执行的早期执行此操作! (在设置拦截器之前定义的过程没有被注意到; Tcl的语义纯粹是可操作的。)幸运的是,你可以使用执行跟踪。

proc procCalled {cmd code args} {
    if {$code==0} {
        global procInFile
        set procName [uplevel 1 [list namespace which [lindex $cmd 1]]]
        set procInFile($procName) [file normalize [info script]]
    }
}
# We use a leave trace for maximum correctness
trace add execution proc leave procCalled

然后,在您想知道调用者的命令上使用另一个执行跟踪,以查找该命令的调用内容,从而查找定义的位置。

proc callingA args {
    # Wrap in a catch so a lookup failure doesn't cause problems
    if {[catch {
        set caller [lindex [info level -1] 0]
        global procInFile
        set file $procInFile($caller)
        puts "Calling [lindex $args 0 0] from $caller in $file"
    }]} {
        # Not called from procedure!
        puts "Calling [lindex $args 0 0] from within [file normalize [info script]]"
    }
}
trace add execution A enter callingA