我刚刚在Donal Fellows上阅读了question的精彩回复。它是如此翔实。
但我对此部分有疑问:never use a dynamic expression with if, for or while as that will suppress a lot of compilation.
我读过两次,但不要以为我完全明白了。
Donal或其他人,你能再详细说明一下吗?
[ UPDATE1 ] 出于好奇,我在tkcon内部尝试了Donal在答复中给出的例子:
% set a {1||0}
1||0
% set b {[exit 1]}
[exit 1]
% expr {$a + $b}
can't use non-numeric string as operand of "+"
% expr $a + $b
1
有趣的是,为什么“expr $a + $b
”会以“1”结尾? “expr $a + $b
”是否已扩展为“expr 1||0 + [exit 1]
”?如果我只是运行扩展版本,tkcon就会关闭,因为[exit 1]
运行会让我感觉到。
[ UPDATE2 ]我仍然在UPDATE1中思考我的问题。正如所建议的那样,我又做了一个实验:
% concat $a + $b
1||0 + [exit 1]
% expr 1||0 + [exit 1]
...tkcon closes...
tkcon关闭是我的预期,仍然想知道为什么expr $a + $b
产生1。
答案 0 :(得分:1)
你强烈反对以这种方式编写表达式,因为很容易弄错并创建(潜在的)安全漏洞。
set x 1
set y {[exec echo >@stdout rm -rf /]}; # Assume this string has come from the user
expr $x+$y
# After Tcl language substitution, it's equivalent to this:
# expr {1+[exec echo >@stdout rm -rf /]}
# If you're not sure why that might be a problem, think a little more...
它也没有强大的编译,因为Tcl的字节码编译器(通常)不进行大量的常量折叠,而是获得一个带有字符串的操作码的调用,并且编译在运行时将字符串转换为字节码以供执行。它既不高效也不安全。
然而,还有更多。如果我们改为看这个:
if $x==$y {
# ...
}
if
的主体是不是编译的,因为if
编译器代码只是看到替换和挽救,推回事物(有效地)解释模式执行。这会减慢if
的整个部分。如果你正在做组合表达(出于安全原因我不鼓励),那么至少要这样做:
if {[expr $x==$y]} {
# ...
}
这至少使if
保持在有效模式。 (它在语义上是等同的。)
首先,expr
。
% tcl::unsupported::disassemble script {expr $x+$y}
ByteCode 0x0x1008b2210, refCt 1, epoch 96, interp 0x0x100829a10 (epoch 96)
Source "expr $x+$y"
Cmds 1, src 10, inst 12, litObjs 3, aux 0, stkDepth 3, code/src 0.00
Commands 1:
1: pc 0-10, src 0-9
Command 1: "expr $x+$y"
(0) push1 0 # "x"
(2) loadStk
(3) push1 1 # "+"
(5) push1 2 # "y"
(7) loadStk
(8) strcat 3
(10) exprStk
(11) done
% tcl::unsupported::disassemble script {expr {$x+$y}}
ByteCode 0x0x1008eb610, refCt 1, epoch 96, interp 0x0x100829a10 (epoch 96)
Source "expr {$x+$y}"
Cmds 1, src 12, inst 8, litObjs 2, aux 0, stkDepth 2, code/src 0.00
Commands 1:
1: pc 0-6, src 0-11
Command 1: "expr {$x+$y}"
(0) push1 0 # "x"
(2) loadStk
(3) push1 1 # "y"
(5) loadStk
(6) add
(7) done
请注意,在第一个版本中,我们使用exprStk
(一般字符串操作),而第二个版本使用add
(它知道它正在使用数字并抛出错误)。
然后,if
。
% tcl::unsupported::disassemble script {if $x==$y {
incr hiya
}}
ByteCode 0x0x10095e210, refCt 1, epoch 96, interp 0x0x100829a10 (epoch 96)
Source "if $x==$y {\n incr hiya\n "...
Cmds 1, src 35, inst 17, litObjs 5, aux 0, stkDepth 4, code/src 0.00
Commands 1:
1: pc 0-15, src 0-34
Command 1: "if $x==$y {\n incr hiya\n "...
(0) push1 0 # "if"
(2) push1 1 # "x"
(4) loadStk
(5) push1 2 # "=="
(7) push1 3 # "y"
(9) loadStk
(10) strcat 3
(12) push1 4 # "\n incr hiya\n "...
(14) invokeStk1 3
(16) done
% tcl::unsupported::disassemble script {if {[expr $x==$y]} {
incr hiya
}}
ByteCode 0x0x10095cc10, refCt 1, epoch 96, interp 0x0x100829a10 (epoch 96)
Source "if {[expr $x==$y]} {\n incr hiya\n "...
Cmds 3, src 44, inst 32, litObjs 5, aux 0, stkDepth 3, code/src 0.00
Commands 3:
1: pc 0-30, src 0-43 2: pc 0-10, src 5-15
3: pc 14-26, src 29-37
Command 1: "if {[expr $x==$y]} {\n incr hiya\n "...
Command 2: "expr $x==$y"...
(0) push1 0 # "x"
(2) loadStk
(3) push1 1 # "=="
(5) push1 2 # "y"
(7) loadStk
(8) strcat 3
(10) exprStk
(11) nop
(12) jumpFalse1 +17 # pc 29
Command 3: "incr hiya"...
(14) startCommand +13 1 # next cmd at pc 27, 1 cmds start here
(23) push1 3 # "hiya"
(25) incrStkImm +1
(27) jump1 +4 # pc 31
(29) push1 4 # ""
(31) done
请注意第二个版本如何理解它正在进行增量(incrStkImm
)?这对性能有很大帮助,特别是对于更长,更简单的脚本。第一个版本只汇编一个参数列表,并使用invokeStk1
来调用解释的if
实现。
FWIW,“黄金标准”(假设我们不在程序中)是这样的:
% tcl::unsupported::disassemble script {if {$x==$y} {
incr hiya
}}
ByteCode 0x0x1008efb10, refCt 1, epoch 96, interp 0x0x100829a10 (epoch 96)
Source "if {$x==$y} {\n incr hiya\n"...
Cmds 2, src 29, inst 18, litObjs 4, aux 0, stkDepth 2, code/src 0.00
Commands 2:
1: pc 0-16, src 0-28 2: pc 9-12, src 18-26
Command 1: "if {$x==$y} {\n incr hiya\n"...
(0) push1 0 # "x"
(2) loadStk
(3) push1 1 # "y"
(5) loadStk
(6) eq
(7) jumpFalse1 +8 # pc 15
Command 2: "incr hiya"...
(9) push1 2 # "hiya"
(11) incrStkImm +1
(13) jump1 +4 # pc 17
(15) push1 3 # ""
(17) done
为了完整性,在一个过程中(在这种情况下,lambda,但字节码是相同的):
tcl::unsupported::disassemble lambda {{} {if {$x==$y} {
incr hiya
}}}
ByteCode 0x0x1008ecc10, refCt 1, epoch 96, interp 0x0x100829a10 (epoch 96)
Source "if {$x==$y} {\n incr hiya\n"...
Cmds 2, src 29, inst 15, litObjs 1, aux 0, stkDepth 2, code/src 0.00
Proc 0x0x102024610, refCt 1, args 0, compiled locals 3
slot 0, scalar, "x"
slot 1, scalar, "y"
slot 2, scalar, "hiya"
Commands 2:
1: pc 0-13, src 0-28 2: pc 7-9, src 18-26
Command 1: "if {$x==$y} {\n incr hiya\n"...
(0) loadScalar1 %v0 # var "x"
(2) loadScalar1 %v1 # var "y"
(4) eq
(5) jumpFalse1 +7 # pc 12
Command 2: "incr hiya"...
(7) incrScalar1Imm %v2 +1 # var "hiya"
(10) jump1 +4 # pc 14
(12) push1 0 # ""
(14) done