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