Tcl 何时是命令';调用了什么编译函数?
我对TCL执行的理解是,如果定义了命令的编译函数,则在调用其执行函数之前,在执行命令时首先调用它 以命令append为例,下面是它在tclBasic.c中的定义:Tcl 何时是命令';调用了什么编译函数?,tcl,Tcl,我对TCL执行的理解是,如果定义了命令的编译函数,则在调用其执行函数之前,在执行命令时首先调用它 以命令append为例,下面是它在tclBasic.c中的定义: static CONST CmdInfo builtInCmds[] = { {"append", (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd, TclCompileAppendCmd, 1}, 以下是我的测试脚本: $ cat
static CONST CmdInfo builtInCmds[] = {
{"append", (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd,
TclCompileAppendCmd, 1},
以下是我的测试脚本:
$ cat t.tcl
set l [list 1 2 3]
append l 4
我在Tcl CompileAppendCmd和Tcl_AppendObjCmd这两个函数中都添加了gdb断点。我的期望是在Tcl_AppendObjCmd之前命中TclCompileAppendCmd
Gdb的目标是tclsh8.4,参数是t.tcl
我看到的很有趣:
TlcCompileAppendCmd
,在您的示例中)由字节码编译器调用,当它想要为该特定命令的特定实例发出字节码时。如果没有命令的编译函数,字节码编译器也有一个回退:它发出指令来调用标准实现(在本例中为Tcl_AppendObjCmd
;另一个字段中的NULL
会导致Tcl生成thunk,以防有人坚持使用特定的API,但您可以忽略此情况)。这是一个有用的行为,因为它是如何处理I/O之类的操作的;与执行磁盘或网络I/O的开销相比,调用标准命令实现的开销非常小
但是字节码编译器什么时候运行
在一个层面上,它会在Tcl的其他部分要求运行时运行。很简单!但这对您并没有真正的帮助。更重要的是,它会在Tcl在Tcl_Obj
中计算尚未具有字节码类型的脚本值时运行(或者如果保存的字节码表明它用于不同的解析上下文或不同的编译纪元)除非求值请求不通过标记TCL_EVAL_DIRECT
到TCL_EvalObjEx
或TCL_EvalEx
(这是Tcl_EvalObjEx的一个方便的包装器)。正是这个标志给您带来了问题
那面旗子什么时候用
它实际上非常简单:当一些代码被认为只运行一次时使用它,因为编译的成本大于使用解释路径的成本。Tk的bind
命令特别使用它来运行替换脚本回调,但是源代码和mai也使用它n代码tclsh
(基本上是任何使用Tcl\u fsevalfilex
或其前身/wrappersTcl\u FSEvalFile
和Tcl\u EvalFile
)的代码。我不能100%确定这是否是源代码
d上下文的正确选择,但现在就是这样。然而,有一个解决办法(非常!)如果你正在处理循环,那么这是值得的:你可以使用一个你立即调用的过程,或者使用一个应用程序(我推荐最近使用的应用程序),init.tcl
使用这些技巧,这就是你看到它编译东西的原因
不,我们通常不会在Tcl运行之间保存编译后的脚本。我们的编译器速度很快,这并不值得;验证加载的编译代码是否适合当前解释器的成本很高,实际上从源代码重新编译速度更快。我们当前的编译器速度很快(我正在开发一个速度较慢的工具,它可以生成非常好的代码)它包括一个提前编译器,但主要是为了商业部署而不是为了速度而覆盖代码。尝试将测试放在进程中。Q2有一些错误的假设。append
命令是编译的。这是Tcl字节码编译器的一个怪癖。一般来说,一次一个脚本开始变长,创建一个主过程并将所有内容放入其中以便编译。EDA工具中发现的非常大的脚本除外。这些脚本遇到了一些可怕的内存限制…我的答案严格适用于8.5和8.6,但可转移到8.4