comparison CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/lib/thread2.8.8/ttrace.tcl @ 68:5028fdace37b

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 16:23:26 -0400
parents
children
comparison
equal deleted inserted replaced
67:0e9998148a16 68:5028fdace37b
1 #
2 # ttrace.tcl --
3 #
4 # Copyright (C) 2003 Zoran Vasiljevic, Archiware GmbH. All Rights Reserved.
5 #
6 # See the file "license.terms" for information on usage and redistribution of
7 # this file, and for a DISCLAIMER OF ALL WARRANTIES.
8 # ----------------------------------------------------------------------------
9 #
10 # User level commands:
11 #
12 # ttrace::eval top-level wrapper (ttrace-savvy eval)
13 # ttrace::enable activates registered Tcl command traces
14 # ttrace::disable terminates tracing of Tcl commands
15 # ttrace::isenabled returns true if ttrace is enabled
16 # ttrace::cleanup bring the interp to a pristine state
17 # ttrace::update update interp to the latest trace epoch
18 # ttrace::config setup some configuration options
19 # ttrace::getscript returns a script for initializing interps
20 #
21 # Commands used for/from trace callbacks:
22 #
23 # ttrace::atenable register callback to be done at trace enable
24 # ttrace::atdisable register callback to be done at trace disable
25 # ttrace::addtrace register user-defined tracer callback
26 # ttrace::addscript register user-defined script generator
27 # ttrace::addresolver register user-defined command resolver
28 # ttrace::addcleanup register user-defined cleanup procedures
29 # ttrace::addentry adds one entry into the named trace store
30 # ttrace::getentry returns the entry value from the named store
31 # ttrace::delentry removes the entry from the named store
32 # ttrace::getentries returns all entries from the named store
33 # ttrace::preload register procedures to be preloaded always
34 #
35 #
36 # Limitations:
37 #
38 # o. [namespace forget] is still not implemented
39 # o. [namespace origin cmd] breaks if cmd is not already defined
40 #
41 # I left this deliberately. I didn't want to override the [namespace]
42 # command in order to avoid potential slowdown.
43 #
44
45 namespace eval ttrace {
46
47 # Setup some compatibility wrappers
48 if {[info commands nsv_set] != ""} {
49 variable tvers 0
50 variable mutex ns_mutex
51 variable elock [$mutex create traceepochmutex]
52 # Import the underlying API; faster than recomputing
53 interp alias {} [namespace current]::_array {} nsv_array
54 interp alias {} [namespace current]::_incr {} nsv_incr
55 interp alias {} [namespace current]::_lappend {} nsv_lappend
56 interp alias {} [namespace current]::_names {} nsv_names
57 interp alias {} [namespace current]::_set {} nsv_set
58 interp alias {} [namespace current]::_unset {} nsv_unset
59 } elseif {![catch {
60 variable tvers [package require Thread]
61 }]} {
62 variable mutex thread::mutex
63 variable elock [$mutex create]
64 # Import the underlying API; faster than recomputing
65 interp alias {} [namespace current]::_array {} tsv::array
66 interp alias {} [namespace current]::_incr {} tsv::incr
67 interp alias {} [namespace current]::_lappend {} tsv::lappend
68 interp alias {} [namespace current]::_names {} tsv::names
69 interp alias {} [namespace current]::_set {} tsv::set
70 interp alias {} [namespace current]::_unset {} tsv::unset
71 } else {
72 error "requires NaviServer/AOLserver or Tcl threading extension"
73 }
74
75 # Keep in sync with the Thread package
76 package provide Ttrace 2.8.8
77
78 # Package variables
79 variable resolvers "" ; # List of registered resolvers
80 variable tracers "" ; # List of registered cmd tracers
81 variable scripts "" ; # List of registered script makers
82 variable enables "" ; # List of trace-enable callbacks
83 variable disables "" ; # List of trace-disable callbacks
84 variable preloads "" ; # List of procedure names to preload
85 variable enabled 0 ; # True if trace is enabled
86 variable config ; # Array with config options
87
88 variable epoch -1 ; # The initialization epoch
89 variable cleancnt 0 ; # Counter of registered cleaners
90
91 # Package private namespaces
92 namespace eval resolve "" ; # Commands for resolving commands
93 namespace eval trace "" ; # Commands registered for tracing
94 namespace eval enable "" ; # Commands invoked at trace enable
95 namespace eval disable "" ; # Commands invoked at trace disable
96 namespace eval script "" ; # Commands for generating scripts
97
98 # Exported commands
99 namespace export unknown
100
101 # Initialize ttrace shared state
102 if {[_array exists ttrace] == 0} {
103 _set ttrace lastepoch $epoch
104 _set ttrace epochlist ""
105 }
106
107 # Initially, allow creation of epochs
108 set config(-doepochs) 1
109
110 proc eval {cmd args} {
111 enable
112 set code [catch {uplevel 1 [concat $cmd $args]} result]
113 disable
114 if {$code == 0} {
115 if {[llength [info commands ns_ictl]]} {
116 ns_ictl save [getscript]
117 } else {
118 thread::broadcast {
119 package require Ttrace
120 ttrace::update
121 }
122 }
123 }
124 return -code $code \
125 -errorinfo $::errorInfo -errorcode $::errorCode $result
126 }
127
128 proc config {args} {
129 variable config
130 if {[llength $args] == 0} {
131 array get config
132 } elseif {[llength $args] == 1} {
133 set opt [lindex $args 0]
134 set config($opt)
135 } else {
136 set opt [lindex $args 0]
137 set val [lindex $args 1]
138 set config($opt) $val
139 }
140 }
141
142 proc enable {} {
143 variable config
144 variable tracers
145 variable enables
146 variable enabled
147 incr enabled 1
148 if {$enabled > 1} {
149 return
150 }
151 if {$config(-doepochs) != 0} {
152 variable epoch [_newepoch]
153 }
154 set nsp [namespace current]
155 foreach enabler $enables {
156 enable::_$enabler
157 }
158 foreach trace $tracers {
159 if {[info commands $trace] != ""} {
160 trace add execution $trace leave ${nsp}::trace::_$trace
161 }
162 }
163 }
164
165 proc disable {} {
166 variable enabled
167 variable tracers
168 variable disables
169 incr enabled -1
170 if {$enabled > 0} {
171 return
172 }
173 set nsp [namespace current]
174 foreach disabler $disables {
175 disable::_$disabler
176 }
177 foreach trace $tracers {
178 if {[info commands $trace] != ""} {
179 trace remove execution $trace leave ${nsp}::trace::_$trace
180 }
181 }
182 }
183
184 proc isenabled {} {
185 variable enabled
186 expr {$enabled > 0}
187 }
188
189 proc update {{from -1}} {
190 if {$from == -1} {
191 variable epoch [_set ttrace lastepoch]
192 } else {
193 if {[lsearch [_set ttrace epochlist] $from] == -1} {
194 error "no such epoch: $from"
195 }
196 variable epoch $from
197 }
198 uplevel 1 [getscript]
199 }
200
201 proc getscript {} {
202 variable preloads
203 variable epoch
204 variable scripts
205 append script [_serializensp] \n
206 append script "::namespace eval [namespace current] {" \n
207 append script "::namespace export unknown" \n
208 append script "_useepoch $epoch" \n
209 append script "}" \n
210 foreach cmd $preloads {
211 append script [_serializeproc $cmd] \n
212 }
213 foreach maker $scripts {
214 append script [script::_$maker]
215 }
216 return $script
217 }
218
219 proc cleanup {args} {
220 foreach cmd [info commands resolve::cleaner_*] {
221 uplevel 1 $cmd $args
222 }
223 }
224
225 proc preload {cmd} {
226 variable preloads
227 if {[lsearch $preloads $cmd] == -1} {
228 lappend preloads $cmd
229 }
230 }
231
232 proc atenable {cmd arglist body} {
233 variable enables
234 if {[lsearch $enables $cmd] == -1} {
235 lappend enables $cmd
236 set cmd [namespace current]::enable::_$cmd
237 proc $cmd $arglist $body
238 return $cmd
239 }
240 }
241
242 proc atdisable {cmd arglist body} {
243 variable disables
244 if {[lsearch $disables $cmd] == -1} {
245 lappend disables $cmd
246 set cmd [namespace current]::disable::_$cmd
247 proc $cmd $arglist $body
248 return $cmd
249 }
250 }
251
252 proc addtrace {cmd arglist body} {
253 variable tracers
254 if {[lsearch $tracers $cmd] == -1} {
255 lappend tracers $cmd
256 set tracer [namespace current]::trace::_$cmd
257 proc $tracer $arglist $body
258 if {[isenabled]} {
259 trace add execution $cmd leave $tracer
260 }
261 return $tracer
262 }
263 }
264
265 proc addscript {cmd body} {
266 variable scripts
267 if {[lsearch $scripts $cmd] == -1} {
268 lappend scripts $cmd
269 set cmd [namespace current]::script::_$cmd
270 proc $cmd args $body
271 return $cmd
272 }
273 }
274
275 proc addresolver {cmd arglist body} {
276 variable resolvers
277 if {[lsearch $resolvers $cmd] == -1} {
278 lappend resolvers $cmd
279 set cmd [namespace current]::resolve::$cmd
280 proc $cmd $arglist $body
281 return $cmd
282 }
283 }
284
285 proc addcleanup {body} {
286 variable cleancnt
287 set cmd [namespace current]::resolve::cleaner_[incr cleancnt]
288 proc $cmd args $body
289 return $cmd
290 }
291
292 proc addentry {cmd var val} {
293 variable epoch
294 _set ${epoch}-$cmd $var $val
295 }
296
297 proc delentry {cmd var} {
298 variable epoch
299 set ei $::errorInfo
300 set ec $::errorCode
301 catch {_unset ${epoch}-$cmd $var}
302 set ::errorInfo $ei
303 set ::errorCode $ec
304 }
305
306 proc getentry {cmd var} {
307 variable epoch
308 set ei $::errorInfo
309 set ec $::errorCode
310 if {[catch {_set ${epoch}-$cmd $var} val]} {
311 set ::errorInfo $ei
312 set ::errorCode $ec
313 set val ""
314 }
315 return $val
316 }
317
318 proc getentries {cmd {pattern *}} {
319 variable epoch
320 _array names ${epoch}-$cmd $pattern
321 }
322
323 proc unknown {args} {
324 set cmd [lindex $args 0]
325 if {[uplevel 1 ttrace::_resolve [list $cmd]]} {
326 set c [catch {uplevel 1 $cmd [lrange $args 1 end]} r]
327 } else {
328 set c [catch {uplevel 1 ::tcl::unknown $args} r]
329 }
330 return -code $c -errorcode $::errorCode -errorinfo $::errorInfo $r
331 }
332
333 proc _resolve {cmd} {
334 variable resolvers
335 foreach resolver $resolvers {
336 if {[uplevel 1 [info comm resolve::$resolver] [list $cmd]]} {
337 return 1
338 }
339 }
340 return 0
341 }
342
343 proc _getthread {} {
344 if {[info commands ns_thread] == ""} {
345 thread::id
346 } else {
347 ns_thread getid
348 }
349 }
350
351 proc _getthreads {} {
352 if {[info commands ns_thread] == ""} {
353 return [thread::names]
354 } else {
355 foreach entry [ns_info threads] {
356 lappend threads [lindex $entry 2]
357 }
358 return $threads
359 }
360 }
361
362 proc _newepoch {} {
363 variable elock
364 variable mutex
365 $mutex lock $elock
366 set old [_set ttrace lastepoch]
367 set new [_incr ttrace lastepoch]
368 _lappend ttrace $new [_getthread]
369 if {$old >= 0} {
370 _copyepoch $old $new
371 _delepochs
372 }
373 _lappend ttrace epochlist $new
374 $mutex unlock $elock
375 return $new
376 }
377
378 proc _copyepoch {old new} {
379 foreach var [_names $old-*] {
380 set cmd [lindex [split $var -] 1]
381 _array reset $new-$cmd [_array get $var]
382 }
383 }
384
385 proc _delepochs {} {
386 set tlist [_getthreads]
387 set elist ""
388 foreach epoch [_set ttrace epochlist] {
389 if {[_dropepoch $epoch $tlist] == 0} {
390 lappend elist $epoch
391 } else {
392 _unset ttrace $epoch
393 }
394 }
395 _set ttrace epochlist $elist
396 }
397
398 proc _dropepoch {epoch threads} {
399 set self [_getthread]
400 foreach tid [_set ttrace $epoch] {
401 if {$tid != $self && [lsearch $threads $tid] >= 0} {
402 lappend alive $tid
403 }
404 }
405 if {[info exists alive]} {
406 _set ttrace $epoch $alive
407 return 0
408 } else {
409 foreach var [_names $epoch-*] {
410 _unset $var
411 }
412 return 1
413 }
414 }
415
416 proc _useepoch {epoch} {
417 if {$epoch >= 0} {
418 set tid [_getthread]
419 if {[lsearch [_set ttrace $epoch] $tid] == -1} {
420 _lappend ttrace $epoch $tid
421 }
422 }
423 }
424
425 proc _serializeproc {cmd} {
426 set dargs [info args $cmd]
427 set pbody [info body $cmd]
428 set pargs ""
429 foreach arg $dargs {
430 if {![info default $cmd $arg def]} {
431 lappend pargs $arg
432 } else {
433 lappend pargs [list $arg $def]
434 }
435 }
436 set nsp [namespace qual $cmd]
437 if {$nsp == ""} {
438 set nsp "::"
439 }
440 append res [list ::namespace eval $nsp] " {" \n
441 append res [list ::proc [namespace tail $cmd] $pargs $pbody] \n
442 append res "}" \n
443 }
444
445 proc _serializensp {{nsp ""} {result _}} {
446 upvar $result res
447 if {$nsp == ""} {
448 set nsp [namespace current]
449 }
450 append res [list ::namespace eval $nsp] " {" \n
451 foreach var [info vars ${nsp}::*] {
452 set vname [namespace tail $var]
453 if {[array exists $var] == 0} {
454 append res [list ::variable $vname [set $var]] \n
455 } else {
456 append res [list ::variable $vname] \n
457 append res [list ::array set $vname [array get $var]] \n
458 }
459 }
460 foreach cmd [info procs ${nsp}::*] {
461 append res [_serializeproc $cmd] \n
462 }
463 append res "}" \n
464 foreach nn [namespace children $nsp] {
465 _serializensp $nn res
466 }
467 return $res
468 }
469 }
470
471 #
472 # The code below is ment to be run once during the application start. It
473 # provides implementation of tracing callbacks for some Tcl commands. Users
474 # can supply their own tracer implementations on-the-fly.
475 #
476 # The code below will create traces for the following Tcl commands:
477 # "namespace", "variable", "load", "proc" and "rename"
478 #
479 # Also, the Tcl object extension XOTcl 1.1.0 is handled and all XOTcl related
480 # things, like classes and objects are traced (many thanks to Gustaf Neumann
481 # from XOTcl for his kind help and support).
482 #
483
484 eval {
485
486 #
487 # Register the "load" trace. This will create the following key/value pair
488 # in the "load" store:
489 #
490 # --- key ---- --- value ---
491 # <path_of_loaded_image> <name_of_the_init_proc>
492 #
493 # We normally need only the name_of_the_init_proc for being able to load
494 # the package in other interpreters, but we store the path to the image
495 # file as well.
496 #
497
498 ttrace::addtrace load {cmdline code args} {
499 if {$code != 0} {
500 return
501 }
502 set image [lindex $cmdline 1]
503 set initp [lindex $cmdline 2]
504 if {$initp == ""} {
505 foreach pkg [info loaded] {
506 if {[lindex $pkg 0] == $image} {
507 set initp [lindex $pkg 1]
508 }
509 }
510 }
511 ttrace::addentry load $image $initp
512 }
513
514 ttrace::addscript load {
515 append res "\n"
516 foreach entry [ttrace::getentries load] {
517 set initp [ttrace::getentry load $entry]
518 append res "::load {} $initp" \n
519 }
520 return $res
521 }
522
523 #
524 # Register the "namespace" trace. This will create the following key/value
525 # entry in "namespace" store:
526 #
527 # --- key ---- --- value ---
528 # ::fully::qualified::namespace 1
529 #
530 # It will also fill the "proc" store for procedures and commands imported
531 # in this namespace with following:
532 #
533 # --- key ---- --- value ---
534 # ::fully::qualified::proc [list <ns> "" ""]
535 #
536 # The <ns> is the name of the namespace where the command or procedure is
537 # imported from.
538 #
539
540 ttrace::addtrace namespace {cmdline code args} {
541 if {$code != 0} {
542 return
543 }
544 set nop [lindex $cmdline 1]
545 set cns [uplevel 1 namespace current]
546 if {$cns == "::"} {
547 set cns ""
548 }
549 switch -glob $nop {
550 eva* {
551 set nsp [lindex $cmdline 2]
552 if {![string match "::*" $nsp]} {
553 set nsp ${cns}::$nsp
554 }
555 ttrace::addentry namespace $nsp 1
556 }
557 imp* {
558 # - parse import arguments (skip opt "-force")
559 set opts [lrange $cmdline 2 end]
560 if {[string match "-fo*" [lindex $opts 0]]} {
561 set opts [lrange $cmdline 3 end]
562 }
563 # - register all imported procs and commands
564 foreach opt $opts {
565 if {![string match "::*" [::namespace qual $opt]]} {
566 set opt ${cns}::$opt
567 }
568 # - first import procs
569 foreach entry [ttrace::getentries proc $opt] {
570 set cmd ${cns}::[::namespace tail $entry]
571 set nsp [::namespace qual $entry]
572 set done($cmd) 1
573 set entry [list 0 $nsp "" ""]
574 ttrace::addentry proc $cmd $entry
575 }
576
577 # - then import commands
578 foreach entry [info commands $opt] {
579 set cmd ${cns}::[::namespace tail $entry]
580 set nsp [::namespace qual $entry]
581 if {[info exists done($cmd)] == 0} {
582 set entry [list 0 $nsp "" ""]
583 ttrace::addentry proc $cmd $entry
584 }
585 }
586 }
587 }
588 }
589 }
590
591 ttrace::addscript namespace {
592 append res \n
593 foreach entry [ttrace::getentries namespace] {
594 append res "::namespace eval $entry {}" \n
595 }
596 return $res
597 }
598
599 #
600 # Register the "variable" trace. This will create the following key/value
601 # entry in the "variable" store:
602 #
603 # --- key ---- --- value ---
604 # ::fully::qualified::variable 1
605 #
606 # The variable value itself is ignored at the time of
607 # trace/collection. Instead, we take the real value at the time of script
608 # generation.
609 #
610
611 ttrace::addtrace variable {cmdline code args} {
612 if {$code != 0} {
613 return
614 }
615 set opts [lrange $cmdline 1 end]
616 if {[llength $opts]} {
617 set cns [uplevel 1 namespace current]
618 if {$cns == "::"} {
619 set cns ""
620 }
621 foreach {var val} $opts {
622 if {![string match "::*" $var]} {
623 set var ${cns}::$var
624 }
625 ttrace::addentry variable $var 1
626 }
627 }
628 }
629
630 ttrace::addscript variable {
631 append res \n
632 foreach entry [ttrace::getentries variable] {
633 set cns [namespace qual $entry]
634 set var [namespace tail $entry]
635 append res "::namespace eval $cns {" \n
636 append res "::variable $var"
637 if {[array exists $entry]} {
638 append res "\n::array set $var [list [array get $entry]]" \n
639 } elseif {[info exists $entry]} {
640 append res " [list [set $entry]]" \n
641 } else {
642 append res \n
643 }
644 append res "}" \n
645 }
646 return $res
647 }
648
649
650 #
651 # Register the "rename" trace. It will create the following key/value pair
652 # in "rename" store:
653 #
654 # --- key ---- --- value ---
655 # ::fully::qualified::old ::fully::qualified::new
656 #
657 # The "new" value may be empty, for commands that have been deleted. In
658 # such cases we also remove any traced procedure definitions.
659 #
660
661 ttrace::addtrace rename {cmdline code args} {
662 if {$code != 0} {
663 return
664 }
665 set cns [uplevel 1 namespace current]
666 if {$cns == "::"} {
667 set cns ""
668 }
669 set old [lindex $cmdline 1]
670 if {![string match "::*" $old]} {
671 set old ${cns}::$old
672 }
673 set new [lindex $cmdline 2]
674 if {$new != ""} {
675 if {![string match "::*" $new]} {
676 set new ${cns}::$new
677 }
678 ttrace::addentry rename $old $new
679 } else {
680 ttrace::delentry proc $old
681 }
682 }
683
684 ttrace::addscript rename {
685 append res \n
686 foreach old [ttrace::getentries rename] {
687 set new [ttrace::getentry rename $old]
688 append res "::rename $old {$new}" \n
689 }
690 return $res
691 }
692
693 #
694 # Register the "proc" trace. This will create the following key/value pair
695 # in the "proc" store:
696 #
697 # --- key ---- --- value ---
698 # ::fully::qualified::proc [list <epoch> <ns> <arglist> <body>]
699 #
700 # The <epoch> chages anytime one (re)defines a proc. The <ns> is the
701 # namespace where the command was imported from. If empty, the <arglist>
702 # and <body> will hold the actual procedure definition. See the
703 # "namespace" tracer implementation also.
704 #
705
706 ttrace::addtrace proc {cmdline code args} {
707 if {$code != 0} {
708 return
709 }
710 set cns [uplevel 1 namespace current]
711 if {$cns == "::"} {
712 set cns ""
713 }
714 set cmd [lindex $cmdline 1]
715 if {![string match "::*" $cmd]} {
716 set cmd ${cns}::$cmd
717 }
718 set dargs [info args $cmd]
719 set pbody [info body $cmd]
720 set pargs ""
721 foreach arg $dargs {
722 if {![info default $cmd $arg def]} {
723 lappend pargs $arg
724 } else {
725 lappend pargs [list $arg $def]
726 }
727 }
728 set pdef [ttrace::getentry proc $cmd]
729 if {$pdef == ""} {
730 set epoch -1 ; # never traced before
731 } else {
732 set epoch [lindex $pdef 0]
733 }
734 ttrace::addentry proc $cmd [list [incr epoch] "" $pargs $pbody]
735 }
736
737 ttrace::addscript proc {
738 return {
739 if {[info command ::tcl::unknown] == ""} {
740 rename ::unknown ::tcl::unknown
741 namespace import -force ::ttrace::unknown
742 }
743 if {[info command ::tcl::info] == ""} {
744 rename ::info ::tcl::info
745 }
746 proc ::info args {
747 set cmd [lindex $args 0]
748 set hit [lsearch -glob {commands procs args default body} $cmd*]
749 if {$hit > 1} {
750 if {[catch {uplevel 1 ::tcl::info $args}]} {
751 uplevel 1 ttrace::_resolve [list [lindex $args 1]]
752 }
753 return [uplevel 1 ::tcl::info $args]
754 }
755 if {$hit == -1} {
756 return [uplevel 1 ::tcl::info $args]
757 }
758 set cns [uplevel 1 namespace current]
759 if {$cns == "::"} {
760 set cns ""
761 }
762 set pat [lindex $args 1]
763 if {![string match "::*" $pat]} {
764 set pat ${cns}::$pat
765 }
766 set fns [ttrace::getentries proc $pat]
767 if {[string match $cmd* commands]} {
768 set fns [concat $fns [ttrace::getentries xotcl $pat]]
769 }
770 foreach entry $fns {
771 if {$cns != [namespace qual $entry]} {
772 set lazy($entry) 1
773 } else {
774 set lazy([namespace tail $entry]) 1
775 }
776 }
777 foreach entry [uplevel 1 ::tcl::info $args] {
778 set lazy($entry) 1
779 }
780 array names lazy
781 }
782 }
783 }
784
785 #
786 # Register procedure resolver. This will try to resolve the command in the
787 # current namespace first, and if not found, in global namespace. It also
788 # handles commands imported from other namespaces.
789 #
790
791 ttrace::addresolver resolveprocs {cmd {export 0}} {
792 set cns [uplevel 1 namespace current]
793 set name [namespace tail $cmd]
794 if {$cns == "::"} {
795 set cns ""
796 }
797 if {![string match "::*" $cmd]} {
798 set ncmd ${cns}::$cmd
799 set gcmd ::$cmd
800 } else {
801 set ncmd $cmd
802 set gcmd $cmd
803 }
804 set pdef [ttrace::getentry proc $ncmd]
805 if {$pdef == ""} {
806 set pdef [ttrace::getentry proc $gcmd]
807 if {$pdef == ""} {
808 return 0
809 }
810 set cmd $gcmd
811 } else {
812 set cmd $ncmd
813 }
814 set epoch [lindex $pdef 0]
815 set pnsp [lindex $pdef 1]
816 if {$pnsp != ""} {
817 set nsp [namespace qual $cmd]
818 if {$nsp == ""} {
819 set nsp ::
820 }
821 set cmd ${pnsp}::$name
822 if {[resolveprocs $cmd 1] == 0 && [info commands $cmd] == ""} {
823 return 0
824 }
825 namespace eval $nsp "namespace import -force $cmd"
826 } else {
827 uplevel 0 [list ::proc $cmd [lindex $pdef 2] [lindex $pdef 3]]
828 if {$export} {
829 set nsp [namespace qual $cmd]
830 if {$nsp == ""} {
831 set nsp ::
832 }
833 namespace eval $nsp "namespace export $name"
834 }
835 }
836 variable resolveproc
837 set resolveproc($cmd) $epoch
838 return 1
839 }
840
841 #
842 # For XOTcl, the entire item introspection/tracing is delegated to XOTcl
843 # itself. The xotcl store is filled with this:
844 #
845 # --- key ---- --- value ---
846 # ::fully::qualified::item <body>
847 #
848 # The <body> is the script used to generate the entire item (class,
849 # object). Note that we do not fill in this during code tracing. It is
850 # done during the script generation. In this step, only the placeholder is
851 # set.
852 #
853 # NOTE: we assume all XOTcl commands are imported in global namespace
854 #
855
856 ttrace::atenable XOTclEnabler {args} {
857 if {[info commands ::xotcl::Class] == ""} {
858 return
859 }
860 if {[info commands ::xotcl::_creator] == ""} {
861 ::xotcl::Class create ::xotcl::_creator -instproc create {args} {
862 set result [next]
863 if {![string match ::xotcl::_* $result]} {
864 ttrace::addentry xotcl $result ""
865 }
866 return $result
867 }
868 }
869 ::xotcl::Class instmixin ::xotcl::_creator
870 }
871
872 ttrace::atdisable XOTclDisabler {args} {
873 if { [info commands ::xotcl::Class] == ""
874 || [info commands ::xotcl::_creator] == ""} {
875 return
876 }
877 ::xotcl::Class instmixin ""
878 ::xotcl::_creator destroy
879 }
880
881 set resolver [ttrace::addresolver resolveclasses {classname} {
882 set cns [uplevel 1 namespace current]
883 set script [ttrace::getentry xotcl $classname]
884 if {$script == ""} {
885 set name [namespace tail $classname]
886 if {$cns == "::"} {
887 set script [ttrace::getentry xotcl ::$name]
888 } else {
889 set script [ttrace::getentry xotcl ${cns}::$name]
890 if {$script == ""} {
891 set script [ttrace::getentry xotcl ::$name]
892 }
893 }
894 if {$script == ""} {
895 return 0
896 }
897 }
898 uplevel 1 [list namespace eval $cns $script]
899 return 1
900 }]
901
902 ttrace::addscript xotcl [subst -nocommands {
903 if {![catch {Serializer new} ss]} {
904 foreach entry [ttrace::getentries xotcl] {
905 if {[ttrace::getentry xotcl \$entry] == ""} {
906 ttrace::addentry xotcl \$entry [\$ss serialize \$entry]
907 }
908 }
909 \$ss destroy
910 return {::xotcl::Class proc __unknown name {$resolver \$name}}
911 }
912 }]
913
914 #
915 # Register callback to be called on cleanup. This will trash lazily loaded
916 # procs which have changed since.
917 #
918
919 ttrace::addcleanup {
920 variable resolveproc
921 foreach cmd [array names resolveproc] {
922 set def [ttrace::getentry proc $cmd]
923 if {$def != ""} {
924 set new [lindex $def 0]
925 set old $resolveproc($cmd)
926 if {[info command $cmd] != "" && $new != $old} {
927 catch {rename $cmd ""}
928 }
929 }
930 }
931 }
932 }
933
934 # EOF
935 return
936
937 # Local Variables:
938 # mode: tcl
939 # fill-column: 78
940 # tab-width: 8
941 # indent-tabs-mode: nil
942 # End: