diff options
author | mlafon <mlafon@gmail.com> | 2017-05-04 22:27:23 (GMT) |
---|---|---|
committer | mlafon <mlafon@gmail.com> | 2017-05-04 22:27:23 (GMT) |
commit | 45bf0120ad12d84441a0c3133a9aa88c446afdc0 (patch) | |
tree | c5fa202ff0effa2d836caa70c515e9376bb7f25f | |
parent | aa7f26cefe717461f284fb9e389ee6a613b2d970 (diff) | |
download | tcl-info_linkedname.zip tcl-info_linkedname.tar.gz tcl-info_linkedname.tar.bz2 |
Add [info linkedname] introspection commandinfo_linkedname
-rw-r--r-- | doc/info.n | 5 | ||||
-rw-r--r-- | generic/tclCmdIL.c | 1 | ||||
-rw-r--r-- | generic/tclExecute.c | 4 | ||||
-rw-r--r-- | generic/tclInt.h | 15 | ||||
-rw-r--r-- | generic/tclVar.c | 180 | ||||
-rw-r--r-- | tests/info.test | 33 |
6 files changed, 201 insertions, 37 deletions
@@ -297,6 +297,11 @@ scripts are stored. This is actually the value of the \fBtcl_library\fR variable and may be changed by setting \fBtcl_library\fR. .TP +\fBinfo linkedname \fIvarname\fR +. +\fIVarname\fR must be a link variable. +Returns the name of the variable it is linked to. +.TP \fBinfo loaded \fR?\fIinterp\fR? ?\fIpackage\fR? . Returns the filename loaded as part of \fIpackage\fR. If \fIpackage\fR diff --git a/generic/tclCmdIL.c b/generic/tclCmdIL.c index a7a5f43..5fa62b3 100644 --- a/generic/tclCmdIL.c +++ b/generic/tclCmdIL.c @@ -175,6 +175,7 @@ static const EnsembleImplMap defaultInfoMap[] = { {"hostname", InfoHostnameCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0}, {"level", InfoLevelCmd, TclCompileInfoLevelCmd, NULL, NULL, 0}, {"library", InfoLibraryCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0}, + {"linkedname", TclInfoLinkedNameCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, {"loaded", InfoLoadedCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0}, {"locals", TclInfoLocalsCmd, TclCompileBasic0Or1ArgCmd, NULL, NULL, 0}, {"nameofexecutable", InfoNameOfExecutableCmd, TclCompileBasic0ArgCmd, NULL, NULL, 0}, diff --git a/generic/tclExecute.c b/generic/tclExecute.c index cfcdd26..e7b55e2 100644 --- a/generic/tclExecute.c +++ b/generic/tclExecute.c @@ -4217,9 +4217,7 @@ TEBCresume( goto gotError; } TclSetVarArray(varPtr); - varPtr->value.tablePtr = ckalloc(sizeof(TclVarHashTable)); - TclInitVarHashTable(varPtr->value.tablePtr, - TclGetVarNsPtr(varPtr)); + TclInitArrayHashTable(varPtr); #ifdef TCL_COMPILE_DEBUG TRACE_APPEND(("done\n")); } else { diff --git a/generic/tclInt.h b/generic/tclInt.h index 725280c..9a30ca2 100644 --- a/generic/tclInt.h +++ b/generic/tclInt.h @@ -208,14 +208,15 @@ typedef struct NamespacePathEntry NamespacePathEntry; /* * Special hashtable for variables: this is just a Tcl_HashTable with an nsPtr - * field added at the end: in this way variables can find their namespace - * without having to copy a pointer in their struct: they can access it via - * their hPtr->tablePtr. + * field and a Var field added at the end: in this way variables can find their + * namespace or related array variable (for array elements) without having to + * copy a pointer in their struct: they can access it via their hPtr->tablePtr. */ typedef struct TclVarHashTable { Tcl_HashTable table; struct Namespace *nsPtr; + struct Var *arrayPtr; } TclVarHashTable; /* @@ -850,6 +851,11 @@ typedef struct VarInHash { ? ((TclVarHashTable *) ((((VarInHash *) (varPtr))->entry.tablePtr)))->nsPtr \ : NULL) +#define TclGetVarArrayPtr(varPtr) \ + (TclIsVarInHash(varPtr) \ + ? ((TclVarHashTable *) ((((VarInHash *) (varPtr))->entry.tablePtr)))->arrayPtr \ + : NULL) + #define VarHashRefCount(varPtr) \ ((VarInHash *) (varPtr))->refCount @@ -3002,11 +3008,14 @@ MODULE_SCOPE int TclInfoCoroutineCmd(ClientData dummy, Tcl_Interp *interp, MODULE_SCOPE Tcl_Obj * TclInfoFrame(Tcl_Interp *interp, CmdFrame *framePtr); MODULE_SCOPE int TclInfoGlobalsCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); +MODULE_SCOPE int TclInfoLinkedNameCmd(ClientData dummy, + Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); MODULE_SCOPE int TclInfoLocalsCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); MODULE_SCOPE int TclInfoVarsCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]); MODULE_SCOPE void TclInitAlloc(void); +MODULE_SCOPE void TclInitArrayHashTable(Var *arrayPtr); MODULE_SCOPE void TclInitDbCkalloc(void); MODULE_SCOPE void TclInitDoubleConversion(void); MODULE_SCOPE void TclInitEmbeddedConfigurationInformation( diff --git a/generic/tclVar.c b/generic/tclVar.c index 1947c8d..c3f63f6 100644 --- a/generic/tclVar.c +++ b/generic/tclVar.c @@ -135,6 +135,7 @@ static const char *badNamespace = "parent namespace doesn't exist"; static const char *missingName = "missing variable name"; static const char *isArrayElement = "name refers to an element in an array"; +static const char *needVarLink = "variable isn't a link"; /* * A test to see if we are in a call frame that has local variables. This is @@ -199,6 +200,8 @@ static Var * VerifyArray(Tcl_Interp *interp, Tcl_Obj *varNameObj); MODULE_SCOPE Var * TclLookupSimpleVar(Tcl_Interp *interp, Tcl_Obj *varNamePtr, int flags, const int create, const char **errMsgPtr, int *indexPtr); +MODULE_SCOPE int TclGetVarName(Tcl_Interp *interp, Var *varPtr, + Tcl_Obj *objPtr); static Tcl_DupInternalRepProc DupLocalVarName; static Tcl_FreeInternalRepProc FreeLocalVarName; @@ -968,8 +971,6 @@ TclLookupArrayElement( { int isNew; Var *varPtr; - TclVarHashTable *tablePtr; - Namespace *nsPtr; /* * We're dealing with an array element. Make sure the variable is an array @@ -1003,15 +1004,7 @@ TclLookupArrayElement( } TclSetVarArray(arrayPtr); - tablePtr = ckalloc(sizeof(TclVarHashTable)); - arrayPtr->value.tablePtr = tablePtr; - - if (TclIsVarInHash(arrayPtr) && TclGetVarNsPtr(arrayPtr)) { - nsPtr = TclGetVarNsPtr(arrayPtr); - } else { - nsPtr = NULL; - } - TclInitVarHashTable(arrayPtr->value.tablePtr, nsPtr); + TclInitArrayHashTable(arrayPtr); } else if (!TclIsVarArray(arrayPtr)) { if (flags & TCL_LEAVE_ERR_MSG) { TclObjVarErrMsg(interp, arrayNamePtr, elNamePtr, msg, needArray, @@ -1048,6 +1041,72 @@ TclLookupArrayElement( /* *---------------------------------------------------------------------- * + * TclGetVarName -- + * + * Appends the name of a Tcl variable to the objPtr object. + * + * Results: + * Returns 1 if the variable name was found, 0 otherwise. + * + * Side effects: + * If found, the variable's name is appended to the string + * representation of objPtr. + * + *---------------------------------------------------------------------- + */ + +int +TclGetVarName(Tcl_Interp *interp, Var *varPtr, Tcl_Obj *objPtr) +{ + Interp *iPtr = (Interp *) interp; + CallFrame *varFramePtr; + Tcl_Obj *objNamePtr; + int isElement; + + if (TclIsVarInHash(varPtr)) { + if (TclIsVarDeadHash(varPtr)) { + return 0; + } + + isElement = TclIsVarArrayElement(varPtr); + if (isElement) { + Var *arrayPtr = TclGetVarArrayPtr(varPtr); + if (arrayPtr) { + TclGetVarName(interp, arrayPtr, objPtr); + } + Tcl_AppendToObj(objPtr, "(", 1); + } + + objNamePtr = VarHashGetKey(varPtr); + Tcl_AppendObjToObj(objPtr, objNamePtr); + + if (isElement) { + Tcl_AppendToObj(objPtr, ")", 1); + } + + return 1; + } + + /* Find varPtr in compiled locals from current or upper call frames. */ + for (varFramePtr = iPtr->varFramePtr; + varFramePtr != NULL ; + varFramePtr = varFramePtr->callerVarPtr) { + + int index = varPtr - varFramePtr->compiledLocals; + + if (index >= 0 && index < varFramePtr->numCompiledLocals) { + objNamePtr = localName(varFramePtr, index); + Tcl_AppendObjToObj(objPtr, objNamePtr); + return 1; + } + } + + return 0; +} + +/* + *---------------------------------------------------------------------- + * * Tcl_GetVar -- * * Return the value of a Tcl variable as a string. @@ -2825,8 +2884,7 @@ TclArraySet( } } TclSetVarArray(varPtr); - varPtr->value.tablePtr = ckalloc(sizeof(TclVarHashTable)); - TclInitVarHashTable(varPtr->value.tablePtr, TclGetVarNsPtr(varPtr)); + TclInitArrayHashTable(varPtr); return TCL_OK; } @@ -4441,7 +4499,6 @@ Tcl_GetVariableFullName( { Interp *iPtr = (Interp *) interp; register Var *varPtr = (Var *) variable; - Tcl_Obj *namePtr; Namespace *nsPtr; if (!varPtr || TclIsVarArrayElement(varPtr)) { @@ -4460,19 +4517,7 @@ Tcl_GetVariableFullName( Tcl_AppendToObj(objPtr, "::", 2); } } - if (TclIsVarInHash(varPtr)) { - if (!TclIsVarDeadHash(varPtr)) { - namePtr = VarHashGetKey(varPtr); - Tcl_AppendObjToObj(objPtr, namePtr); - } - } else if (iPtr->varFramePtr->procPtr) { - int index = varPtr - iPtr->varFramePtr->compiledLocals; - - if (index >= 0 && index < iPtr->varFramePtr->numCompiledLocals) { - namePtr = localName(iPtr->varFramePtr, index); - Tcl_AppendObjToObj(objPtr, namePtr); - } - } + TclGetVarName(interp, varPtr, objPtr); } /* @@ -5812,6 +5857,71 @@ TclInfoGlobalsCmd( /* *---------------------------------------------------------------------- * + * TclInfoLinkedNameCmd -- + * + * Called to implement the "info linkedname" command that returns the + * name of a link variable. Handles the following syntax: + * + * info linkedname varname + * + * Results: + * Returns TCL_OK if successful and TCL_ERROR if there is an error. + * + * Side effects: + * Returns a result in the interpreter's result object. If there is an + * error, the result is an error message. + * + *---------------------------------------------------------------------- + */ + +int +TclInfoLinkedNameCmd( + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ + int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ +{ + Tcl_Obj *varName, *objNamePtr; + Var *varPtr; + const char *errMsg; + int index; + + if (objc != 2) { + Tcl_WrongNumArgs(interp, 1, objv, "varname"); + return TCL_ERROR; + } + + varName = objv[1]; + varPtr = TclLookupSimpleVar(interp, varName, TCL_AVOID_RESOLVERS, + /* create */ 0, &errMsg, &index); + + if (varPtr && !TclIsVarLink(varPtr)) { + errMsg = needVarLink; + varPtr = NULL; + } + + if (varPtr == NULL) { + TclObjVarErrMsg(interp, varName, NULL, "access", errMsg, -1); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARLINK", + TclGetString(varName), NULL); + return TCL_ERROR; + } + + objNamePtr = Tcl_NewObj(); + if (TclGetVarName(interp, varPtr->value.linkPtr, objNamePtr) != 1) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "unable to find linked name for \"%s\"", TclGetString(varName))); + TclDecrRefCount(objNamePtr); + return TCL_ERROR; + } + + Tcl_SetObjResult(interp, objNamePtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * * TclInfoLocalsCmd -- (moved over from tclCmdIl.c) * * Called to implement the "info locals" command to return a list of @@ -6015,6 +6125,22 @@ TclInitVarHashTable( Tcl_InitCustomHashTable(&tablePtr->table, TCL_CUSTOM_TYPE_KEYS, &tclVarHashKeyType); tablePtr->nsPtr = nsPtr; + tablePtr->arrayPtr = NULL; +} + +void +TclInitArrayHashTable( + Var *arrayPtr) +{ + TclVarHashTable *tablePtr; + + tablePtr = ckalloc(sizeof(TclVarHashTable)); + + Tcl_InitCustomHashTable(&tablePtr->table, + TCL_CUSTOM_TYPE_KEYS, &tclVarHashKeyType); + tablePtr->nsPtr = TclGetVarNsPtr(arrayPtr); + tablePtr->arrayPtr = arrayPtr; + arrayPtr->value.tablePtr = tablePtr; } static Tcl_HashEntry * diff --git a/tests/info.test b/tests/info.test index fd89b47..38b7887 100644 --- a/tests/info.test +++ b/tests/info.test @@ -678,16 +678,16 @@ test info-21.1 {miscellaneous error conditions} -returnCodes error -body { } -result {wrong # args: should be "info subcommand ?arg ...?"} test info-21.2 {miscellaneous error conditions} -returnCodes error -body { info gorp -} -result {unknown or ambiguous subcommand "gorp": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} +} -result {unknown or ambiguous subcommand "gorp": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, linkedname, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} test info-21.3 {miscellaneous error conditions} -returnCodes error -body { info c -} -result {unknown or ambiguous subcommand "c": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} +} -result {unknown or ambiguous subcommand "c": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, linkedname, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} test info-21.4 {miscellaneous error conditions} -returnCodes error -body { info l -} -result {unknown or ambiguous subcommand "l": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} +} -result {unknown or ambiguous subcommand "l": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, linkedname, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} test info-21.5 {miscellaneous error conditions} -returnCodes error -body { info s -} -result {unknown or ambiguous subcommand "s": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} +} -result {unknown or ambiguous subcommand "s": must be args, body, class, cmdcount, commands, complete, coroutine, default, errorstack, exists, frame, functions, globals, hostname, level, library, linkedname, loaded, locals, nameofexecutable, object, patchlevel, procs, script, sharedlibextension, tclversion, or vars} ## # ### ### ### ######### ######### ######### @@ -2415,6 +2415,31 @@ test info-39.0 {Bug 4b61afd660} -setup { rename probe {} } -result 3 +# ------------------------------------------------------------------------- +# info linkedname +test info-41.0 {info linkedname - valid} -body { + proc getname {var} { upvar 1 $var up ; return [info linkedname up] } + set i 1 + getname i +} -result {i} +test info-41.1 {info linkedname - valid} -body { + proc getname {var} { upvar 1 $var up ; return [info linkedname up] } + proc p {i} { return [getname i] } + p 2 +} -result {i} +test info-41.2 {info linkedname - valid} -body { + proc getname {var} { upvar 1 $var up ; return [info linkedname up] } + array set a {1 one 2 two} + getname a(1) +} -result {a(1)} +test info-41.3 {info linkedname - no such variable} -body { + info linkedname foo +} -returnCodes error -result {can't access "foo": no such variable} +test info-41.4 {info linkedname - not a varlink object} -body { + set i 5 + info linkedname i +} -returnCodes error -result {can't access "i": variable isn't a link} + # cleanup catch {namespace delete test_ns_info1 test_ns_info2} ::tcltest::cleanupTests |