2015-04-16 46 views

回答

1

假设你已经有一个正在运行的Tcl解释器,并且只有source脚本来运行它(避免描述整个解释器的启动顺序和初始化),所以最近(8.x)Tcl解释器中会发生以下情况。

这基本上调用一些C代码样Tcl_EvalFile()它加载脚本到内存中,而实际上做执行到内部TclEvalEx(),这是Tcl_EvalEx()私人版本工作的手,记录在这里(https://www.tcl.tk/man/tcl/TclLib/Eval.htm)。

这解释了没有字节码编译的顶层脚本,例如,脚本被解析,一旦找到命令,它就会直接执行。这很慢,而且旧版Tcl 7.x一直在运行。

但是,如果执行命令,则会检查命令是否可以字节编译为更高效的形式。这是通过proc定义的命令的情况,所以如果第一次执行命令,则Tcl_EvalObjEx()在内部调用TclCompileObj()以获取该命令的字节码表示,该命令缓存在解析命令的Tcl_Obj内。

如果该命令不能编译,则直接执行该命令,例如,调用generic/tclCmd*.c中的函数之一,例如处理命令concat该命令将在generic/tclCmdAH.c中为Tcl_ConcatObjCmd()。这个函数也可能来自一些加载到解释器中的C扩展,并且完全以相同的方式处理。可用的命令在一些哈希表内部注册,这就是Tcl_CreateObjCmd()所做的。

但是,如果命令可以编译,会发生不同的事情。在这种情况下,该命令转变为字节码表示。例如,对于这样一个简单的过程:proc c {a b} {concat $a $b}这会变成四个字节码。实际上,你可以检查通过::tcl::unsupported::disassemble命令生成的字节码,看看这个Tkcon会话使用Tcl的8.6.3:

() 69 % proc c {a b} {concat $a $b} 
() 70 % ::tcl::unsupported::disassemble proc c 
ByteCode 0x00000000045A2660, refCt 1, epoch 16, interp 0x0000000002E26120 (epoch 16) 
    Source "concat $a $b" 
    Cmds 1, src 12, inst 10, litObjs 0, aux 0, stkDepth 2, code/src 0.00 
    Proc 0x0000000002EB32A0, refCt 1, args 2, compiled locals 2 
     slot 0, scalar, arg, "a" 
     slot 1, scalar, arg, "b" 
    Commands 1: 
     1: pc 0-8, src 0-11 
    Command 1: "concat $a $b" 
    (0) loadScalar1 %v0  # var "a" 
    (2) loadScalar1 %v1  # var "b" 
    (4) concatStk 2 
    (9) done 

这字节码(四线在底部)是由内部的虚拟机执行Tcl解释器。这是目前基于堆栈的虚拟机。您可以在generic/tclExecute.c文件中找到它的实现。

并非所有的命令都可以进行字节编码,如果命令没有匹配的字节码,则产生对普通函数的调用,例如,直接执行中提到的Tcl_*ObjCmd之一。

在大多数情况下(几乎每次命令被重用时),字节码都要快得多。这是建议将所有代码放在过程中的一个原因(它们以这种方式进行字节编译),并加上表达式。

希望这可以说明这个过程。我省略了一些更复杂的细节,比如编译epoches以及协同例程的问题,非递归引擎等。

期望与普通的Tcl完全相同,它只是通过Tcl_CreateObjCmd()添加了一些额外的命令。