Index: generic/tclBasic.c ================================================================== --- generic/tclBasic.c +++ generic/tclBasic.c @@ -4814,15 +4814,58 @@ Namespace *currNsPtr = NULL;/* Used to check for and invoke any registered * unknown command handler for the current * namespace (TIP 181). */ Namespace *savedNsPtr = NULL; + Tcl_Size qualLen; + const char *qualName = TclGetStringFromObj(objv[0], &qualLen); + currNsPtr = varFramePtr->nsPtr; - if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) { - currNsPtr = iPtr->globalNsPtr; - if (currNsPtr == NULL) { - Tcl_Panic("TEOV_NotFound: NULL global namespace pointer"); + if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL) || + (qualLen > 2 && memchr(qualName, ':', qualLen)) /* fast check for NS:: */ + ) { + /* + * first try to find namespace unknown handler of the namespace + * of executed command if available: + */ + Namespace *altNsPtr, *dummyNsPtr; + const char *simpleName; + + (void) TclGetNamespaceForQualName(interp, qualName, currNsPtr, + TCL_FIND_IF_NOT_SIMPLE, &currNsPtr, &altNsPtr, + &dummyNsPtr, &simpleName); + if (!simpleName) { + goto globNS; + } + if (!currNsPtr || (currNsPtr == iPtr->globalNsPtr)) { + if (!altNsPtr || (altNsPtr == iPtr->globalNsPtr)) { + goto globNS; + } + currNsPtr = altNsPtr; + } + while (currNsPtr->unknownHandlerPtr == NULL || + (currNsPtr->flags & (NS_DYING | NS_DEAD)) + ) { + /* traverse to alive parent namespace containing handler */ + if (!(currNsPtr = currNsPtr->parentPtr) || + (currNsPtr == iPtr->globalNsPtr) + ) { + /* continue from alternate NS if available */ + if (!altNsPtr || (altNsPtr == iPtr->globalNsPtr)) { + goto globNS; + } + currNsPtr = altNsPtr; + altNsPtr = NULL; + continue; + globNS: + /* fallback to the global unknown */ + currNsPtr = iPtr->globalNsPtr; + if (currNsPtr == NULL) { + Tcl_Panic("TEOV_NotFound: NULL global namespace pointer"); + } + break; + } } } /* * Check to see if the resolution namespace has lost its unknown handler. Index: library/init.tcl ================================================================== --- library/init.tcl +++ library/init.tcl @@ -116,10 +116,22 @@ } namespace inscope ::tcl::clock [list namespace ensemble create -command \ [uplevel 1 [list ::namespace origin [::lindex [info level 0] 0]]] \ -map $cmdmap] ::tcl::unsupported::clock::configure -init-complete + + # Auto-loading stubs for 'clock.tcl' + + namespace inscope ::tcl::clock { + proc _load_stubs args { + namespace unknown {} + ::source -encoding utf-8 [::file join [info library] clock.tcl] + tailcall {*}$args + } + namespace unknown ::tcl::clock::_load_stubs + } + uplevel 1 [info level 0] } } # Conditionalize for presence of exec. Index: library/tclIndex ================================================================== --- library/tclIndex +++ library/tclIndex @@ -17,33 +17,10 @@ set auto_index(::auto_mkindex_parser::childhook) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::command) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::commandInit) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::fullname) [list ::tcl::Pkg::source [file join $dir auto.tcl]] set auto_index(::auto_mkindex_parser::indexEntry) [list ::tcl::Pkg::source [file join $dir auto.tcl]] -set auto_index(::tcl::clock::Initialize) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::mcget) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::mcMerge) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::GetSystemLocale) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::EnterLocale) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::_hasRegistry) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::LoadWindowsDateTimeFormats) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::LocalizeFormat) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::GetSystemTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::SetupTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::GuessWindowsTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::LoadTimeZoneFile) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::LoadZoneinfoFile) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::ReadZoneinfoFile) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::ParsePosixTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::ProcessPosixTimeZone) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::DeterminePosixDSTTime) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::GetJulianDayFromEraYearDay) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::GetJulianDayFromEraYearMonthWeekDay) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::IsGregorianLeapYear) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::WeekdayOnOrBefore) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::ChangeCurrentLocale) [list ::tcl::Pkg::source [file join $dir clock.tcl]] -set auto_index(::tcl::clock::ClearCaches) [list ::tcl::Pkg::source [file join $dir clock.tcl]] set auto_index(foreachLine) [list ::tcl::Pkg::source [file join $dir foreachline.tcl]] set auto_index(::tcl::history) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(history) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistAdd) [list ::tcl::Pkg::source [file join $dir history.tcl]] set auto_index(::tcl::HistKeep) [list ::tcl::Pkg::source [file join $dir history.tcl]] Index: tests/namespace.test ================================================================== --- tests/namespace.test +++ tests/namespace.test @@ -3122,10 +3122,52 @@ namespace unknown } } -cleanup { namespace delete foo } -result ok +test namespace-52.13 {unknown: invocation outside of NS doesn't evade namespace unknown, bug 910d67a229fe7f65} -body { + namespace eval ::foo::bar { + proc _unknown args {list ::foo:bar:_unknown [uplevel {namespace current}] $args} + namespace unknown [namespace current]::_unknown + } + list [namespace inscope ::foo::bar {xxx}] [namespace inscope ::foo {bar::xxx}] [::foo::bar::xxx] [namespace inscope :: {foo::bar::xxx}] +} -cleanup { + namespace delete ::foo +} -result {{::foo:bar:_unknown ::foo::bar xxx} {::foo:bar:_unknown ::foo bar::xxx} {::foo:bar:_unknown :: ::foo::bar::xxx} {::foo:bar:_unknown :: foo::bar::xxx}} +test namespace-52.14 {unknown: invocation outside of NS doesn't evade namespace unknown for command with sub-NS, bug 910d67a229fe7f65} -body { + namespace eval ::foo::bar { + proc _unknown args {list ::foo:bar:_unknown [uplevel {namespace current}] $args} + namespace unknown [namespace current]::_unknown + } + set res {} + lappend res [namespace inscope ::foo::bar {xxx::yyy}] [namespace inscope ::foo {bar::xxx::yyy}] [::foo::bar::xxx::yyy] [namespace inscope :: {foo::bar::xxx::yyy}] + # now with existsing ::foo::bar::xxx, but without unknown handler inside (only parent ::foo::bar has a handler): + namespace eval ::foo::bar::xxx {} + lappend res [namespace inscope ::foo::bar {xxx::yyy}] [namespace inscope ::foo {bar::xxx::yyy}] [::foo::bar::xxx::yyy] [namespace inscope :: {foo::bar::xxx::yyy}] +} -cleanup { + namespace delete ::foo + unset -nocomplain res +} -result [lrepeat 2 \ + {::foo:bar:_unknown ::foo::bar xxx::yyy} {::foo:bar:_unknown ::foo bar::xxx::yyy} {::foo:bar:_unknown :: ::foo::bar::xxx::yyy} {::foo:bar:_unknown :: foo::bar::xxx::yyy} +] +test namespace-52.14 {unknown: it must consider alternate search path (relative global NS), bug 910d67a229fe7f65} -body { + namespace eval ::foo::bar {} + namespace eval ::xxx::yyy { + proc _unknown args {list ::xxx:yyy:_unknown [uplevel {namespace current}] $args} + namespace unknown [namespace current]::_unknown + } + set res {} + lappend res [namespace inscope ::foo::bar {xxx::yyy::cmd}] [namespace inscope ::foo {xxx::yyy::cmd}] + namespace eval ::foo::bar::xxx {} + lappend res [namespace inscope ::foo::bar {xxx::yyy::cmd}] [namespace inscope ::foo {xxx::yyy::cmd}] + namespace eval ::foo::bar::xxx::yyy {} + lappend res [namespace inscope ::foo::bar {xxx::yyy::cmd}] [namespace inscope ::foo {xxx::yyy::cmd}] +} -cleanup { + namespace delete ::foo + namespace delete ::xxx + unset -nocomplain res +} -result [lrepeat 3 {::xxx:yyy:_unknown ::foo::bar xxx::yyy::cmd} {::xxx:yyy:_unknown ::foo xxx::yyy::cmd}] # TIP 314 - ensembles with parameters test namespace-53.1 {ensembles: parameters} { namespace eval ns { namespace export x