Tcl字节码和触发器

时间:2017-03-14 16:38:48

标签: tcl bytecode

我在c中使用tcl,一般来说,我们为每次执行tcl脚本创建一个新的命名空间。 如果我在脚本中使用proc,它会在同一个命名空间下吗? proc如何转换成字节码?它是否在调用者的相同名称空间下转换?它会在不同的命名空间中,并可能使字节码无效吗?

请您简单解释一下tcl字节码是什么以及我们如何从哈希中获取变量(局部和全局变量)以及upvar如何影响它?

非常感谢!!!

1 个答案:

答案 0 :(得分:2)

Tcl中的每个过程都知道它的名称空间是什么;它与包含其名称的命名空间相同(总是有一个;全局命名空间称为::)。因此,为每次执行创建一个新的命名空间可能没什么意义。

字节码本身(在Tcl源代码中tclExecute.c中定义的一种堆栈计算机上执行)在需要时创建,通常是在您第一次执行该过程时。您可以使用tcl::unsupported::disassemble命令打印字节码:

% proc example {x} {
    return [expr {$x * 2 + 3}]
}
% puts [tcl::unsupported::disassemble proc example]
ByteCode 0x0x1008d1b10, refCt 1, epoch 15, interp 0x0x100829a10 (epoch 15)
  Source "\n    return [expr {$x * 2 + 3}]"...
  Cmds 2, src 32, inst 9, litObjs 2, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0x103028010, refCt 1, args 1, compiled locals 1
      slot 0, scalar, arg, "x"
  Commands 2:
      1: pc 0-8, src 5-30        2: pc 0-7, src 13-29
  Command 1: "return [expr {$x * 2 + 3}]"...
  Command 2: "expr {$x * 2 + 3}"...
    (0) loadScalar1 %v0     # var "x"
    (2) push1 0     # "2"
    (4) mult 
    (5) push1 1     # "3"
    (7) add 
    (8) done 

新版本的Tcl 8.6还支持tcl::unsupported::getbytecode,它提供对相同类型信息的机器可读访问。你真的不想解析disassemble的输出。

从不同的命名空间调用过程不会使该过程的字节码无效。 (为什么会这样?对于库代码而言,它的效率非常低!)但是有些操作知道如何访问外部世界。让我们用upvar

做一个例子
% proc example2 {xvar} {
    upvar 1 $xvar x
    return [expr {[incr x] * 2 + 3}]
}
% puts [tcl::unsupported::disassemble proc example2]
ByteCode 0x0x10300e610, refCt 1, epoch 15, interp 0x0x100829a10 (epoch 15)
  Source "\n    upvar 1 $xvar x\n    return [expr {[incr x] * 2 +"...
  Cmds 4, src 58, inst 33, litObjs 4, aux 0, stkDepth 2, code/src 0.00
  Proc 0x0x103028390, refCt 1, args 1, compiled locals 2
      slot 0, scalar, arg, "xvar"
      slot 1, scalar, "x"
  Commands 4:
      1: pc 0-12, src 5-19        2: pc 13-31, src 25-56
      3: pc 22-30, src 33-55        4: pc 22-24, src 40-45
  Command 1: "upvar 1 $xvar x"...
    (0) push1 0     # "1"
    (2) loadScalar1 %v0     # var "xvar"
    (4) upvar %v1   # var "x"
    (9) pop 
    (10) nop 
    (11) nop 
    (12) nop 
  Command 2: "return [expr {[incr x] * 2 + 3}]"...
    (13) startCommand +19 3     # next cmd at pc 32, 3 cmds start here
  Command 3: "expr {[incr x] * 2 + 3}"...
  Command 4: "incr x"...
    (22) incrScalar1Imm %v1 +1  # var "x"
    (25) push1 2    # "2"
    (27) mult 
    (28) push1 3    # "3"
    (30) add 
    (31) done 
    (32) done 

upvar本身的操作顺序是在堆栈上推送level参数和远程变量的名称(在这种情况下来自作为参数传入的变量),然后是{{ 1}}将索引1处的局部变量表条目绑定到范围1(调用者)中的变量,该变量由来自upvar %v1的名称调用。通过使局部变量实际上是指向另一个变量的指针来完成绑定;一旦制成,它是高效的。 xvar命令使用类似的机制,但稍有不同(global操作码绑定到命名命名空间中的变量。)

有一些操作会使字节码无效,但它们就像重定位具有编译功能的过程或命令一样。如果你没有使用nsupvar,你可能不需要担心它(它完全是自动的)。

在程序中执行rename(通常来自另一个程序)有点不同,因为那时你按名称查找变量。局部变量表的名称表是过程元数据的一部分,并且不在其中的任何变量都存储在哈希表中(当您在使用不用于其他名称的名称的过程中有upvar时使用它)作为proc中的变量的目的;即使它不常见也会发生。)

如果您真的想要详细信息,那么就无法替代阅读Tcl源代码。字节码在很多地方生成,但它的核心是upvar;相关的执行引擎位于tclCompile.c。程序在tclExecute.ctclProc.c中的名称空间和tclNamesp.c中的变量中定义。可能还有其他相关的地方,但那些是主要的。