summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--generic/tclBasic.c51
-rw-r--r--tests/namespace.test42
2 files changed, 89 insertions, 4 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c
index b01717e..509e63c 100644
--- a/generic/tclBasic.c
+++ b/generic/tclBasic.c
@@ -5183,11 +5183,54 @@ TEOV_NotFound(
* namespace (TIP 181). */
Namespace *savedNsPtr = NULL;
+ int 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("Tcl_EvalObjv: 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;
+ }
}
}
diff --git a/tests/namespace.test b/tests/namespace.test
index c98ad4a..11a17cb 100644
--- a/tests/namespace.test
+++ b/tests/namespace.test
@@ -3112,6 +3112,48 @@ test namespace-52.12 {unknown: error case must not reset handler} -body {
} -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} {