Chaque procédure dans Tcl sait quel est son espace de noms; c'est la même chose que l'espace de noms contenant son nom (il y en a toujours un, l'espace de noms global est appelé ::
). En tant que tel, cela n'a probablement pas beaucoup de sens de créer un nouvel espace de noms pour chaque exécution. Le bytecode lui-même (qui s'exécute sur une sorte de machine à pile définie en tclExecute.c
dans le code source Tcl) est créé lorsqu'il est nécessaire, ce qui est généralement le cas lorsque vous êtes sur le point d'exécuter la procédure pour la première fois. Vous pouvez imprimer le bytecode en utilisant la commande 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
versions Nouveau-assez de Tcl 8.6 prennent également en charge tcl::unsupported::getbytecode
, qui fournit un accès lisible par une machine aux mêmes sortes d'informations. Vous ne voulez pas vraiment analyser la sortie de disassemble
.
L'appel d'une procédure à partir d'un espace de noms différent n'invalide pas le bytecode pour cette procédure. (Pourquoi cela serait-il désespérément inefficace pour le code de la bibliothèque!) Mais il existe des opérations qui savent comment accéder au monde extérieur. Faisons un exemple avec 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
La séquence des opérations pour upvar
lui-même de pousser sur la pile le paramètre de niveau et le nom de la variable à distance (qui, dans ce cas provient d'une variable passée en tant argument), puis le upvar %v1
qui lie l'entrée de table de variable locale à l'index 1 à la variable de portée 1 (l'appelant) appelée par le nom qui vient de xvar
. La liaison est faite en faisant en fait la variable locale être un pointeur vers l'autre variable; une fois fabriqué, il est très efficace. La commande global
utilise un mécanisme similaire mais légèrement différent (l'opcode nsupvar
se lie à une variable dans un espace de nom nommé).
Il existe quelques opérations qui invalident le bytecode, mais il s'agit de relocaliser une procédure ou une commande dotée d'une fonction de compilation. Si vous n'utilisez pas rename
, vous n'aurez probablement jamais besoin de vous en préoccuper (et c'est entièrement automatique).
Faire un upvar
dans une procédure (généralement d'une autre procédure) est un peu différent, puisque vous regardez des variables par leur nom. La table de noms pour la table de variables locale fait partie des métadonnées de procédure, et toute variable qui n'y figure pas est stockée dans une table de hachage (utilisée lorsque vous avez un upvar
dans la procédure qui utilise un nom non utilisé à d'autres fins comme variable à l'intérieur de ce proc, cela peut arriver même si ce n'est pas très commun).
Si vous voulez vraiment les détails, il n'y a pas de substitut pour lire le code source Tcl.Le bytecode est généré dans plusieurs endroits, mais le cœur de celui-ci est tclCompile.c
; le moteur d'exécution associé est au tclExecute.c
. Les procédures sont définies dans tclProc.c
, les espaces de noms dans tclNamesp.c
et les variables dans tclVar.c
. Il y a probablement d'autres endroits pertinents à regarder, mais ce sont les principaux.