123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942 |
- #
- # ttrace.tcl --
- #
- # Copyright (C) 2003 Zoran Vasiljevic, Archiware GmbH. All Rights Reserved.
- #
- # See the file "license.terms" for information on usage and redistribution of
- # this file, and for a DISCLAIMER OF ALL WARRANTIES.
- # ----------------------------------------------------------------------------
- #
- # User level commands:
- #
- # ttrace::eval top-level wrapper (ttrace-savvy eval)
- # ttrace::enable activates registered Tcl command traces
- # ttrace::disable terminates tracing of Tcl commands
- # ttrace::isenabled returns true if ttrace is enabled
- # ttrace::cleanup bring the interp to a pristine state
- # ttrace::update update interp to the latest trace epoch
- # ttrace::config setup some configuration options
- # ttrace::getscript returns a script for initializing interps
- #
- # Commands used for/from trace callbacks:
- #
- # ttrace::atenable register callback to be done at trace enable
- # ttrace::atdisable register callback to be done at trace disable
- # ttrace::addtrace register user-defined tracer callback
- # ttrace::addscript register user-defined script generator
- # ttrace::addresolver register user-defined command resolver
- # ttrace::addcleanup register user-defined cleanup procedures
- # ttrace::addentry adds one entry into the named trace store
- # ttrace::getentry returns the entry value from the named store
- # ttrace::delentry removes the entry from the named store
- # ttrace::getentries returns all entries from the named store
- # ttrace::preload register procedures to be preloaded always
- #
- #
- # Limitations:
- #
- # o. [namespace forget] is still not implemented
- # o. [namespace origin cmd] breaks if cmd is not already defined
- #
- # I left this deliberately. I didn't want to override the [namespace]
- # command in order to avoid potential slowdown.
- #
- namespace eval ttrace {
- # Setup some compatibility wrappers
- if {[info commands nsv_set] != ""} {
- variable tvers 0
- variable mutex ns_mutex
- variable elock [$mutex create traceepochmutex]
- # Import the underlying API; faster than recomputing
- interp alias {} [namespace current]::_array {} nsv_array
- interp alias {} [namespace current]::_incr {} nsv_incr
- interp alias {} [namespace current]::_lappend {} nsv_lappend
- interp alias {} [namespace current]::_names {} nsv_names
- interp alias {} [namespace current]::_set {} nsv_set
- interp alias {} [namespace current]::_unset {} nsv_unset
- } elseif {![catch {
- variable tvers [package require Thread]
- }]} {
- variable mutex thread::mutex
- variable elock [$mutex create]
- # Import the underlying API; faster than recomputing
- interp alias {} [namespace current]::_array {} tsv::array
- interp alias {} [namespace current]::_incr {} tsv::incr
- interp alias {} [namespace current]::_lappend {} tsv::lappend
- interp alias {} [namespace current]::_names {} tsv::names
- interp alias {} [namespace current]::_set {} tsv::set
- interp alias {} [namespace current]::_unset {} tsv::unset
- } else {
- error "requires NaviServer/AOLserver or Tcl threading extension"
- }
- # Keep in sync with the Thread package
- package provide Ttrace 2.8.7
- # Package variables
- variable resolvers "" ; # List of registered resolvers
- variable tracers "" ; # List of registered cmd tracers
- variable scripts "" ; # List of registered script makers
- variable enables "" ; # List of trace-enable callbacks
- variable disables "" ; # List of trace-disable callbacks
- variable preloads "" ; # List of procedure names to preload
- variable enabled 0 ; # True if trace is enabled
- variable config ; # Array with config options
- variable epoch -1 ; # The initialization epoch
- variable cleancnt 0 ; # Counter of registered cleaners
- # Package private namespaces
- namespace eval resolve "" ; # Commands for resolving commands
- namespace eval trace "" ; # Commands registered for tracing
- namespace eval enable "" ; # Commands invoked at trace enable
- namespace eval disable "" ; # Commands invoked at trace disable
- namespace eval script "" ; # Commands for generating scripts
- # Exported commands
- namespace export unknown
- # Initialize ttrace shared state
- if {[_array exists ttrace] == 0} {
- _set ttrace lastepoch $epoch
- _set ttrace epochlist ""
- }
- # Initially, allow creation of epochs
- set config(-doepochs) 1
- proc eval {cmd args} {
- enable
- set code [catch {uplevel 1 [concat $cmd $args]} result]
- disable
- if {$code == 0} {
- if {[llength [info commands ns_ictl]]} {
- ns_ictl save [getscript]
- } else {
- thread::broadcast {
- package require Ttrace
- ttrace::update
- }
- }
- }
- return -code $code \
- -errorinfo $::errorInfo -errorcode $::errorCode $result
- }
- proc config {args} {
- variable config
- if {[llength $args] == 0} {
- array get config
- } elseif {[llength $args] == 1} {
- set opt [lindex $args 0]
- set config($opt)
- } else {
- set opt [lindex $args 0]
- set val [lindex $args 1]
- set config($opt) $val
- }
- }
- proc enable {} {
- variable config
- variable tracers
- variable enables
- variable enabled
- incr enabled 1
- if {$enabled > 1} {
- return
- }
- if {$config(-doepochs) != 0} {
- variable epoch [_newepoch]
- }
- set nsp [namespace current]
- foreach enabler $enables {
- enable::_$enabler
- }
- foreach trace $tracers {
- if {[info commands $trace] != ""} {
- trace add execution $trace leave ${nsp}::trace::_$trace
- }
- }
- }
- proc disable {} {
- variable enabled
- variable tracers
- variable disables
- incr enabled -1
- if {$enabled > 0} {
- return
- }
- set nsp [namespace current]
- foreach disabler $disables {
- disable::_$disabler
- }
- foreach trace $tracers {
- if {[info commands $trace] != ""} {
- trace remove execution $trace leave ${nsp}::trace::_$trace
- }
- }
- }
- proc isenabled {} {
- variable enabled
- expr {$enabled > 0}
- }
- proc update {{from -1}} {
- if {$from == -1} {
- variable epoch [_set ttrace lastepoch]
- } else {
- if {[lsearch [_set ttrace epochlist] $from] == -1} {
- error "no such epoch: $from"
- }
- variable epoch $from
- }
- uplevel [getscript]
- }
- proc getscript {} {
- variable preloads
- variable epoch
- variable scripts
- append script [_serializensp] \n
- append script "::namespace eval [namespace current] {" \n
- append script "::namespace export unknown" \n
- append script "_useepoch $epoch" \n
- append script "}" \n
- foreach cmd $preloads {
- append script [_serializeproc $cmd] \n
- }
- foreach maker $scripts {
- append script [script::_$maker]
- }
- return $script
- }
- proc cleanup {args} {
- foreach cmd [info commands resolve::cleaner_*] {
- uplevel $cmd $args
- }
- }
- proc preload {cmd} {
- variable preloads
- if {[lsearch $preloads $cmd] == -1} {
- lappend preloads $cmd
- }
- }
- proc atenable {cmd arglist body} {
- variable enables
- if {[lsearch $enables $cmd] == -1} {
- lappend enables $cmd
- set cmd [namespace current]::enable::_$cmd
- proc $cmd $arglist $body
- return $cmd
- }
- }
- proc atdisable {cmd arglist body} {
- variable disables
- if {[lsearch $disables $cmd] == -1} {
- lappend disables $cmd
- set cmd [namespace current]::disable::_$cmd
- proc $cmd $arglist $body
- return $cmd
- }
- }
- proc addtrace {cmd arglist body} {
- variable tracers
- if {[lsearch $tracers $cmd] == -1} {
- lappend tracers $cmd
- set tracer [namespace current]::trace::_$cmd
- proc $tracer $arglist $body
- if {[isenabled]} {
- trace add execution $cmd leave $tracer
- }
- return $tracer
- }
- }
- proc addscript {cmd body} {
- variable scripts
- if {[lsearch $scripts $cmd] == -1} {
- lappend scripts $cmd
- set cmd [namespace current]::script::_$cmd
- proc $cmd args $body
- return $cmd
- }
- }
- proc addresolver {cmd arglist body} {
- variable resolvers
- if {[lsearch $resolvers $cmd] == -1} {
- lappend resolvers $cmd
- set cmd [namespace current]::resolve::$cmd
- proc $cmd $arglist $body
- return $cmd
- }
- }
- proc addcleanup {body} {
- variable cleancnt
- set cmd [namespace current]::resolve::cleaner_[incr cleancnt]
- proc $cmd args $body
- return $cmd
- }
- proc addentry {cmd var val} {
- variable epoch
- _set ${epoch}-$cmd $var $val
- }
- proc delentry {cmd var} {
- variable epoch
- set ei $::errorInfo
- set ec $::errorCode
- catch {_unset ${epoch}-$cmd $var}
- set ::errorInfo $ei
- set ::errorCode $ec
- }
- proc getentry {cmd var} {
- variable epoch
- set ei $::errorInfo
- set ec $::errorCode
- if {[catch {_set ${epoch}-$cmd $var} val]} {
- set ::errorInfo $ei
- set ::errorCode $ec
- set val ""
- }
- return $val
- }
- proc getentries {cmd {pattern *}} {
- variable epoch
- _array names ${epoch}-$cmd $pattern
- }
- proc unknown {args} {
- set cmd [lindex $args 0]
- if {[uplevel ttrace::_resolve [list $cmd]]} {
- set c [catch {uplevel $cmd [lrange $args 1 end]} r]
- } else {
- set c [catch {::eval ::tcl::unknown $args} r]
- }
- return -code $c -errorcode $::errorCode -errorinfo $::errorInfo $r
- }
- proc _resolve {cmd} {
- variable resolvers
- foreach resolver $resolvers {
- if {[uplevel [info comm resolve::$resolver] [list $cmd]]} {
- return 1
- }
- }
- return 0
- }
- proc _getthread {} {
- if {[info commands ns_thread] == ""} {
- thread::id
- } else {
- ns_thread getid
- }
- }
- proc _getthreads {} {
- if {[info commands ns_thread] == ""} {
- return [thread::names]
- } else {
- foreach entry [ns_info threads] {
- lappend threads [lindex $entry 2]
- }
- return $threads
- }
- }
- proc _newepoch {} {
- variable elock
- variable mutex
- $mutex lock $elock
- set old [_set ttrace lastepoch]
- set new [_incr ttrace lastepoch]
- _lappend ttrace $new [_getthread]
- if {$old >= 0} {
- _copyepoch $old $new
- _delepochs
- }
- _lappend ttrace epochlist $new
- $mutex unlock $elock
- return $new
- }
- proc _copyepoch {old new} {
- foreach var [_names $old-*] {
- set cmd [lindex [split $var -] 1]
- _array reset $new-$cmd [_array get $var]
- }
- }
- proc _delepochs {} {
- set tlist [_getthreads]
- set elist ""
- foreach epoch [_set ttrace epochlist] {
- if {[_dropepoch $epoch $tlist] == 0} {
- lappend elist $epoch
- } else {
- _unset ttrace $epoch
- }
- }
- _set ttrace epochlist $elist
- }
- proc _dropepoch {epoch threads} {
- set self [_getthread]
- foreach tid [_set ttrace $epoch] {
- if {$tid != $self && [lsearch $threads $tid] >= 0} {
- lappend alive $tid
- }
- }
- if {[info exists alive]} {
- _set ttrace $epoch $alive
- return 0
- } else {
- foreach var [_names $epoch-*] {
- _unset $var
- }
- return 1
- }
- }
- proc _useepoch {epoch} {
- if {$epoch >= 0} {
- set tid [_getthread]
- if {[lsearch [_set ttrace $epoch] $tid] == -1} {
- _lappend ttrace $epoch $tid
- }
- }
- }
- proc _serializeproc {cmd} {
- set dargs [info args $cmd]
- set pbody [info body $cmd]
- set pargs ""
- foreach arg $dargs {
- if {![info default $cmd $arg def]} {
- lappend pargs $arg
- } else {
- lappend pargs [list $arg $def]
- }
- }
- set nsp [namespace qual $cmd]
- if {$nsp == ""} {
- set nsp "::"
- }
- append res [list ::namespace eval $nsp] " {" \n
- append res [list ::proc [namespace tail $cmd] $pargs $pbody] \n
- append res "}" \n
- }
- proc _serializensp {{nsp ""} {result _}} {
- upvar $result res
- if {$nsp == ""} {
- set nsp [namespace current]
- }
- append res [list ::namespace eval $nsp] " {" \n
- foreach var [info vars ${nsp}::*] {
- set vname [namespace tail $var]
- if {[array exists $var] == 0} {
- append res [list ::variable $vname [set $var]] \n
- } else {
- append res [list ::variable $vname] \n
- append res [list ::array set $vname [array get $var]] \n
- }
- }
- foreach cmd [info procs ${nsp}::*] {
- append res [_serializeproc $cmd] \n
- }
- append res "}" \n
- foreach nn [namespace children $nsp] {
- _serializensp $nn res
- }
- return $res
- }
- }
- #
- # The code below is ment to be run once during the application start. It
- # provides implementation of tracing callbacks for some Tcl commands. Users
- # can supply their own tracer implementations on-the-fly.
- #
- # The code below will create traces for the following Tcl commands:
- # "namespace", "variable", "load", "proc" and "rename"
- #
- # Also, the Tcl object extension XOTcl 1.1.0 is handled and all XOTcl related
- # things, like classes and objects are traced (many thanks to Gustaf Neumann
- # from XOTcl for his kind help and support).
- #
- eval {
- #
- # Register the "load" trace. This will create the following key/value pair
- # in the "load" store:
- #
- # --- key ---- --- value ---
- # <path_of_loaded_image> <name_of_the_init_proc>
- #
- # We normally need only the name_of_the_init_proc for being able to load
- # the package in other interpreters, but we store the path to the image
- # file as well.
- #
- ttrace::addtrace load {cmdline code args} {
- if {$code != 0} {
- return
- }
- set image [lindex $cmdline 1]
- set initp [lindex $cmdline 2]
- if {$initp == ""} {
- foreach pkg [info loaded] {
- if {[lindex $pkg 0] == $image} {
- set initp [lindex $pkg 1]
- }
- }
- }
- ttrace::addentry load $image $initp
- }
- ttrace::addscript load {
- append res "\n"
- foreach entry [ttrace::getentries load] {
- set initp [ttrace::getentry load $entry]
- append res "::load {} $initp" \n
- }
- return $res
- }
- #
- # Register the "namespace" trace. This will create the following key/value
- # entry in "namespace" store:
- #
- # --- key ---- --- value ---
- # ::fully::qualified::namespace 1
- #
- # It will also fill the "proc" store for procedures and commands imported
- # in this namespace with following:
- #
- # --- key ---- --- value ---
- # ::fully::qualified::proc [list <ns> "" ""]
- #
- # The <ns> is the name of the namespace where the command or procedure is
- # imported from.
- #
- ttrace::addtrace namespace {cmdline code args} {
- if {$code != 0} {
- return
- }
- set nop [lindex $cmdline 1]
- set cns [uplevel namespace current]
- if {$cns == "::"} {
- set cns ""
- }
- switch -glob $nop {
- eva* {
- set nsp [lindex $cmdline 2]
- if {![string match "::*" $nsp]} {
- set nsp ${cns}::$nsp
- }
- ttrace::addentry namespace $nsp 1
- }
- imp* {
- # - parse import arguments (skip opt "-force")
- set opts [lrange $cmdline 2 end]
- if {[string match "-fo*" [lindex $opts 0]]} {
- set opts [lrange $cmdline 3 end]
- }
- # - register all imported procs and commands
- foreach opt $opts {
- if {![string match "::*" [::namespace qual $opt]]} {
- set opt ${cns}::$opt
- }
- # - first import procs
- foreach entry [ttrace::getentries proc $opt] {
- set cmd ${cns}::[::namespace tail $entry]
- set nsp [::namespace qual $entry]
- set done($cmd) 1
- set entry [list 0 $nsp "" ""]
- ttrace::addentry proc $cmd $entry
- }
- # - then import commands
- foreach entry [info commands $opt] {
- set cmd ${cns}::[::namespace tail $entry]
- set nsp [::namespace qual $entry]
- if {[info exists done($cmd)] == 0} {
- set entry [list 0 $nsp "" ""]
- ttrace::addentry proc $cmd $entry
- }
- }
- }
- }
- }
- }
- ttrace::addscript namespace {
- append res \n
- foreach entry [ttrace::getentries namespace] {
- append res "::namespace eval $entry {}" \n
- }
- return $res
- }
- #
- # Register the "variable" trace. This will create the following key/value
- # entry in the "variable" store:
- #
- # --- key ---- --- value ---
- # ::fully::qualified::variable 1
- #
- # The variable value itself is ignored at the time of
- # trace/collection. Instead, we take the real value at the time of script
- # generation.
- #
- ttrace::addtrace variable {cmdline code args} {
- if {$code != 0} {
- return
- }
- set opts [lrange $cmdline 1 end]
- if {[llength $opts]} {
- set cns [uplevel namespace current]
- if {$cns == "::"} {
- set cns ""
- }
- foreach {var val} $opts {
- if {![string match "::*" $var]} {
- set var ${cns}::$var
- }
- ttrace::addentry variable $var 1
- }
- }
- }
- ttrace::addscript variable {
- append res \n
- foreach entry [ttrace::getentries variable] {
- set cns [namespace qual $entry]
- set var [namespace tail $entry]
- append res "::namespace eval $cns {" \n
- append res "::variable $var"
- if {[array exists $entry]} {
- append res "\n::array set $var [list [array get $entry]]" \n
- } elseif {[info exists $entry]} {
- append res " [list [set $entry]]" \n
- } else {
- append res \n
- }
- append res "}" \n
- }
- return $res
- }
- #
- # Register the "rename" trace. It will create the following key/value pair
- # in "rename" store:
- #
- # --- key ---- --- value ---
- # ::fully::qualified::old ::fully::qualified::new
- #
- # The "new" value may be empty, for commands that have been deleted. In
- # such cases we also remove any traced procedure definitions.
- #
- ttrace::addtrace rename {cmdline code args} {
- if {$code != 0} {
- return
- }
- set cns [uplevel namespace current]
- if {$cns == "::"} {
- set cns ""
- }
- set old [lindex $cmdline 1]
- if {![string match "::*" $old]} {
- set old ${cns}::$old
- }
- set new [lindex $cmdline 2]
- if {$new != ""} {
- if {![string match "::*" $new]} {
- set new ${cns}::$new
- }
- ttrace::addentry rename $old $new
- } else {
- ttrace::delentry proc $old
- }
- }
- ttrace::addscript rename {
- append res \n
- foreach old [ttrace::getentries rename] {
- set new [ttrace::getentry rename $old]
- append res "::rename $old {$new}" \n
- }
- return $res
- }
- #
- # Register the "proc" trace. This will create the following key/value pair
- # in the "proc" store:
- #
- # --- key ---- --- value ---
- # ::fully::qualified::proc [list <epoch> <ns> <arglist> <body>]
- #
- # The <epoch> chages anytime one (re)defines a proc. The <ns> is the
- # namespace where the command was imported from. If empty, the <arglist>
- # and <body> will hold the actual procedure definition. See the
- # "namespace" tracer implementation also.
- #
- ttrace::addtrace proc {cmdline code args} {
- if {$code != 0} {
- return
- }
- set cns [uplevel namespace current]
- if {$cns == "::"} {
- set cns ""
- }
- set cmd [lindex $cmdline 1]
- if {![string match "::*" $cmd]} {
- set cmd ${cns}::$cmd
- }
- set dargs [info args $cmd]
- set pbody [info body $cmd]
- set pargs ""
- foreach arg $dargs {
- if {![info default $cmd $arg def]} {
- lappend pargs $arg
- } else {
- lappend pargs [list $arg $def]
- }
- }
- set pdef [ttrace::getentry proc $cmd]
- if {$pdef == ""} {
- set epoch -1 ; # never traced before
- } else {
- set epoch [lindex $pdef 0]
- }
- ttrace::addentry proc $cmd [list [incr epoch] "" $pargs $pbody]
- }
- ttrace::addscript proc {
- return {
- if {[info command ::tcl::unknown] == ""} {
- rename ::unknown ::tcl::unknown
- namespace import -force ::ttrace::unknown
- }
- if {[info command ::tcl::info] == ""} {
- rename ::info ::tcl::info
- }
- proc ::info args {
- set cmd [lindex $args 0]
- set hit [lsearch -glob {commands procs args default body} $cmd*]
- if {$hit > 1} {
- if {[catch {uplevel ::tcl::info $args}]} {
- uplevel ttrace::_resolve [list [lindex $args 1]]
- }
- return [uplevel ::tcl::info $args]
- }
- if {$hit == -1} {
- return [uplevel ::tcl::info $args]
- }
- set cns [uplevel namespace current]
- if {$cns == "::"} {
- set cns ""
- }
- set pat [lindex $args 1]
- if {![string match "::*" $pat]} {
- set pat ${cns}::$pat
- }
- set fns [ttrace::getentries proc $pat]
- if {[string match $cmd* commands]} {
- set fns [concat $fns [ttrace::getentries xotcl $pat]]
- }
- foreach entry $fns {
- if {$cns != [namespace qual $entry]} {
- set lazy($entry) 1
- } else {
- set lazy([namespace tail $entry]) 1
- }
- }
- foreach entry [uplevel ::tcl::info $args] {
- set lazy($entry) 1
- }
- array names lazy
- }
- }
- }
- #
- # Register procedure resolver. This will try to resolve the command in the
- # current namespace first, and if not found, in global namespace. It also
- # handles commands imported from other namespaces.
- #
- ttrace::addresolver resolveprocs {cmd {export 0}} {
- set cns [uplevel namespace current]
- set name [namespace tail $cmd]
- if {$cns == "::"} {
- set cns ""
- }
- if {![string match "::*" $cmd]} {
- set ncmd ${cns}::$cmd
- set gcmd ::$cmd
- } else {
- set ncmd $cmd
- set gcmd $cmd
- }
- set pdef [ttrace::getentry proc $ncmd]
- if {$pdef == ""} {
- set pdef [ttrace::getentry proc $gcmd]
- if {$pdef == ""} {
- return 0
- }
- set cmd $gcmd
- } else {
- set cmd $ncmd
- }
- set epoch [lindex $pdef 0]
- set pnsp [lindex $pdef 1]
- if {$pnsp != ""} {
- set nsp [namespace qual $cmd]
- if {$nsp == ""} {
- set nsp ::
- }
- set cmd ${pnsp}::$name
- if {[resolveprocs $cmd 1] == 0 && [info commands $cmd] == ""} {
- return 0
- }
- namespace eval $nsp "namespace import -force $cmd"
- } else {
- uplevel 0 [list ::proc $cmd [lindex $pdef 2] [lindex $pdef 3]]
- if {$export} {
- set nsp [namespace qual $cmd]
- if {$nsp == ""} {
- set nsp ::
- }
- namespace eval $nsp "namespace export $name"
- }
- }
- variable resolveproc
- set resolveproc($cmd) $epoch
- return 1
- }
- #
- # For XOTcl, the entire item introspection/tracing is delegated to XOTcl
- # itself. The xotcl store is filled with this:
- #
- # --- key ---- --- value ---
- # ::fully::qualified::item <body>
- #
- # The <body> is the script used to generate the entire item (class,
- # object). Note that we do not fill in this during code tracing. It is
- # done during the script generation. In this step, only the placeholder is
- # set.
- #
- # NOTE: we assume all XOTcl commands are imported in global namespace
- #
- ttrace::atenable XOTclEnabler {args} {
- if {[info commands ::xotcl::Class] == ""} {
- return
- }
- if {[info commands ::xotcl::_creator] == ""} {
- ::xotcl::Class create ::xotcl::_creator -instproc create {args} {
- set result [next]
- if {![string match ::xotcl::_* $result]} {
- ttrace::addentry xotcl $result ""
- }
- return $result
- }
- }
- ::xotcl::Class instmixin ::xotcl::_creator
- }
- ttrace::atdisable XOTclDisabler {args} {
- if { [info commands ::xotcl::Class] == ""
- || [info commands ::xotcl::_creator] == ""} {
- return
- }
- ::xotcl::Class instmixin ""
- ::xotcl::_creator destroy
- }
- set resolver [ttrace::addresolver resolveclasses {classname} {
- set cns [uplevel namespace current]
- set script [ttrace::getentry xotcl $classname]
- if {$script == ""} {
- set name [namespace tail $classname]
- if {$cns == "::"} {
- set script [ttrace::getentry xotcl ::$name]
- } else {
- set script [ttrace::getentry xotcl ${cns}::$name]
- if {$script == ""} {
- set script [ttrace::getentry xotcl ::$name]
- }
- }
- if {$script == ""} {
- return 0
- }
- }
- uplevel [list namespace eval $cns $script]
- return 1
- }]
- ttrace::addscript xotcl [subst -nocommands {
- if {![catch {Serializer new} ss]} {
- foreach entry [ttrace::getentries xotcl] {
- if {[ttrace::getentry xotcl \$entry] == ""} {
- ttrace::addentry xotcl \$entry [\$ss serialize \$entry]
- }
- }
- \$ss destroy
- return {::xotcl::Class proc __unknown name {$resolver \$name}}
- }
- }]
- #
- # Register callback to be called on cleanup. This will trash lazily loaded
- # procs which have changed since.
- #
- ttrace::addcleanup {
- variable resolveproc
- foreach cmd [array names resolveproc] {
- set def [ttrace::getentry proc $cmd]
- if {$def != ""} {
- set new [lindex $def 0]
- set old $resolveproc($cmd)
- if {[info command $cmd] != "" && $new != $old} {
- catch {rename $cmd ""}
- }
- }
- }
- }
- }
- # EOF
- return
- # Local Variables:
- # mode: tcl
- # fill-column: 78
- # tab-width: 8
- # indent-tabs-mode: nil
- # End:
|