我正在使用TCL8.6.8。
这是我的实验:
>cat ~/tmp/1.tcl
proc p {} {
foreach i {a b c} {
if {$i == "b"} {
break
}
puts $i
}
}
现在我进入tclsh:
% proc disa {file_name} {
set f [open $file_name r]
set data [read -nonewline $f]
close $f
tcl::unsupported::disassemble script $data
}
% disa ~/tmp/1.tcl
ByteCode 0x0x55cabfc393b0, refCt 1, epoch 17, interp 0x0x55cabfbdd990 (epoch 17)
Source "proc p {} {\nforeach i {a b c} {\n if {$i == \"b\"} ..."
Cmds 1, src 175, inst 11, litObjs 4, aux 0, stkDepth 4, code/src 1.26
Code 220 = header 168+inst 11+litObj 32+exc 0+aux 0+cmdMap 4
Commands 1:
1: pc 0-9, src 0-87
Command 1: "proc p {} {\nforeach i {a b c} {\n if {$i == \"b\"} ..."
(0) push1 0 # "proc"
(2) push1 1 # "p"
(4) push1 2 # ""
(6) push1 3 # "\nforeach i {a b c} {\n if {$i == \"b..."
(8) invokeStk1 4
(10) done
您可以看到它没有完全编译为字节码,因为foreach的嵌套脚本被当作文字字符串。
现在我使用tcl::unsupported::disassemble proc
而不是tcl::unsupported::disassemble script
,我可以获得完整的字节码编译版本:
% source ~/tmp/1.tcl
% tcl::unsupported::disassemble proc p
ByteCode 0x0x55cabfc393b0, refCt 1, epoch 17, interp 0x0x55cabfbdd990 (epoch 17)
Source "\nforeach i {a b c} {\n if {$i == \"b\"} {\n ..."
File "/home/jibin/tmp/1.tcl" Line 1
Cmds 4, src 76, inst 54, litObjs 4, aux 1, stkDepth 5, code/src 4.21
Code 320 = header 168+inst 54+litObj 32+exc 28+aux 16+cmdMap 16
Proc 0x0x55cabfc72820, refCt 1, args 0, compiled locals 1
slot 0, scalar, "i"
Exception ranges 1, depth 1:
0: level 0, loop, pc 7-47, continue 49, break 50
Commands 4:
1: pc 0-52, src 1-74 2: pc 7-41, src 25-60
3: pc 23-36, src 50-54 4: pc 42-47, src 66-72
Command 1: "foreach i {a b c} {\n if {$i == \"b\"} {\n br..."
(0) push1 0 # "a b c"
(2) foreach_start 0
[jumpOffset=-42, vars=[%v0]]
Command 2: "if {$i == \"b\"} {\n break\n ..."
(7) startCommand +34 1 # next cmd at pc 41, 1 cmds start here
(16) loadScalar1 %v0 # var "i"
(18) push1 1 # "b"
(20) eq
(21) jumpFalse1 +18 # pc 39
Command 3: "break..."
(23) startCommand +14 1 # next cmd at pc 37, 1 cmds start here
(32) jump4 +18 # pc 50
(37) jump1 +4 # pc 41
(39) push1 2 # ""
(41) pop
Command 4: "puts $i..."
(42) push1 3 # "puts"
(44) loadScalar1 %v0 # var "i"
(46) invokeStk1 2
(48) pop
(49) foreach_step
(50) foreach_end
(51) push1 2 # ""
(53) done
这是我的问题:tcl::unsupported::disassemble script
为什么不完全编译脚本? foreach
命令位于proc内部,我想proc
的编译功能会调用每个命令的编译功能,因此无论如何都会调用foreach
命令的编译功能。
答案 0 :(得分:1)
Tcl将脚本或过程的编译推迟到第一次需要该脚本/过程的字节编码版本时。编译是相当快的(在有意义的地方仔细地缓存),并且8.6中的优化器是轻量级的(只是杀死了以前生成的一些愚蠢的代码序列),所以通常这不是一个大问题。为特定命令完成的编译程度相差很大:expr
几乎总是经过深度编译(如果可能!),而proc
本身从未被编译;您在反汇编中看到的是通用命令调用编译(将单词推入堆栈,用那么多单词调用通用命令,完成工作)。这是有道理的,因为对proc
的大多数调用仅发生一次,并且只有真正进行设置才能使有趣的事情在以后发生。我们更改proc
本身进行深度编译(与它创建的过程相反)的机会为零,至少在8.7 / 9.0中是这样,而且很可能在那之前。证明要开展的工作是不可能的。
但是,如果您想及早触发过程编译,可以可以。它所需要的只是一点触发...
trace add execution proc leave {apply {{cmdArgs code result op} {
if {$code == 0} {
# proc succeeded; it must have been called as: proc name args body
set procedureName [lindex $cmdArgs 1]
# Make sure we resolve the procedure name in the right namespace!
set compTime [lindex [time {
uplevel 1 [list tcl::unsupported::getbytecode proc $procedureName]
}] 0]
# We're done now! Totally optional print of how long it took…
puts stderr "Compiled $procedure in $compTime µs"
}
}}}
我认为 getbytecode
比disassemble
快一点(它做同样的事情,但是产生机器可读的输出),但是我可能错了。如果要在8.5中使用该代码,则需要使用disassemble
。