Compare commits

...
Author SHA1 Message Date
arjenmarkus ba9050dc8c Turn the trace command into an ensemble, removing the old obsolete forms of the command.
This had consequences for the counting of the arguments, as "trace add" etc. are now a single "word".
2018-12-03 20:32:49 +00:00
arjenmarkus 3f690409ee Remove the obsolete form of the [trace] command from the documentation. 2018-05-08 17:51:56 +00:00
arjenmarkus c32522f739 Changes to the code: remove the macro "TCL_REMOVE_OBSOLETE_TRACES" and associated code.
Turn the [trace] command into an ensemble.

Note: the tests show that not all is well yet, basic-15.2 for instance fails -- the [trace] command 
complains about an invalid operation "::deleter", but if I run the test manually, that is, by typing the
commands into an interactive tclsh based on the new code, then it proceeds without a failure.
(The invocation of [trace] comes from the [history] command)
2018-05-08 17:38:23 +00:00
markus 95c04468f9 Adjust the tests that use the old-style trace command. This is a preparation for the modernisation of the trace command itself and should be compatible with any version of Tcl >= 8.4 2018-05-03 07:12:58 +00:00
markus f5fb2ee8f9 Create new branch named "am-trace-ensemble" 2018-05-02 19:02:11 +00:00
19 changed files with 304 additions and 276 deletions
-21
View File
@@ -354,27 +354,6 @@ containing two elements, which are the \fIopList\fR and \fIcommandPrefix\fR
associated with the trace. If \fIname\fR does not exist or does not
have any traces set, then the result of the command will be an empty
string.
.RE
.PP
For backwards compatibility, three other subcommands are available:
.RS
.TP
\fBtrace variable \fIname ops command\fR
This is equivalent to \fBtrace add variable \fIname ops command\fR.
.TP
\fBtrace vdelete \fIname ops command\fR
This is equivalent to \fBtrace remove variable \fIname ops command\fR
.TP
\fBtrace vinfo \fIname\fR
This is equivalent to \fBtrace info variable \fIname\fR
.RE
.PP
These subcommands are deprecated and will likely be removed in a
future version of Tcl. They use an older syntax in which \fBarray\fR,
\fBread\fR, \fBwrite\fR, \fBunset\fR are replaced by \fBa\fR, \fBr\fR,
\fBw\fR and \fBu\fR respectively, and the \fIops\fR argument is not a
list, but simply a string concatenation of the operations, such as
\fBrwua\fR.
.SH EXAMPLES
.PP
Print a message whenever either of the global variables \fBfoo\fR and
-4
View File
@@ -906,10 +906,6 @@ typedef struct Tcl_DString {
#define TCL_INTERP_DESTROYED 0x100
#define TCL_LEAVE_ERR_MSG 0x200
#define TCL_TRACE_ARRAY 0x800
#ifndef TCL_REMOVE_OBSOLETE_TRACES
/* Required to support old variable/vdelete/vinfo traces. */
#define TCL_TRACE_OLD_STYLE 0x1000
#endif
/* Indicate the semantics of the result of a trace. */
#define TCL_TRACE_RESULT_DYNAMIC 0x8000
#define TCL_TRACE_RESULT_OBJECT 0x10000
+7 -2
View File
@@ -232,7 +232,6 @@ static const CmdInfo builtInCmds[] = {
{"switch", Tcl_SwitchObjCmd, TclCompileSwitchCmd, TclNRSwitchObjCmd, CMD_IS_SAFE},
{"tailcall", NULL, TclCompileTailcallCmd, TclNRTailcallObjCmd, CMD_IS_SAFE},
{"throw", Tcl_ThrowObjCmd, TclCompileThrowCmd, NULL, CMD_IS_SAFE},
{"trace", Tcl_TraceObjCmd, NULL, NULL, CMD_IS_SAFE},
{"try", Tcl_TryObjCmd, TclCompileTryCmd, TclNRTryObjCmd, CMD_IS_SAFE},
{"unset", Tcl_UnsetObjCmd, TclCompileUnsetCmd, NULL, CMD_IS_SAFE},
{"uplevel", Tcl_UplevelObjCmd, NULL, TclNRUplevelObjCmd, CMD_IS_SAFE},
@@ -768,7 +767,7 @@ Tcl_CreateInterp(void)
/*
* Create the "array", "binary", "chan", "clock", "dict", "encoding",
* "file", "info", "namespace" and "string" ensembles. Note that all these
* "file", "info", "namespace", "string" and "trace" ensembles. Note that all these
* commands (and their subcommands that are not present in the global
* namespace) are wholly safe *except* for "clock", "encoding" and "file".
*/
@@ -793,6 +792,12 @@ Tcl_CreateInterp(void)
TclClockInit(interp);
/*
* Register "trace" subcommands - modelled after the "clock" ensemble
*/
TclTraceInit(interp);
/*
* Register the built-in functions. This is empty now that they are
* implemented as commands in the ::tcl::mathfunc namespace.
+2 -4
View File
@@ -3430,9 +3430,7 @@ MODULE_SCOPE int Tcl_ThrowObjCmd(ClientData dummy, Tcl_Interp *interp,
MODULE_SCOPE int Tcl_TimeObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE int Tcl_TraceObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
MODULE_SCOPE void TclTraceInit(Tcl_Interp *interp);
MODULE_SCOPE int Tcl_TryObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const objv[]);
@@ -4018,7 +4016,7 @@ typedef enum TclProcessWaitStatus {
TCL_PROCESS_EXITED = 1, /* Process has exited. */
TCL_PROCESS_SIGNALED = 2, /* Child killed because of a signal. */
TCL_PROCESS_STOPPED = 3, /* Child suspended because of a signal. */
TCL_PROCESS_UNKNOWN_STATUS = 4
TCL_PROCESS_UNKNOWN_STATUS = 4
/* Child wait status didn't make sense. */
} TclProcessWaitStatus;
+225 -175
View File
@@ -100,13 +100,24 @@ static Tcl_TraceTypeObjCmd TraceVariableObjCmd;
static Tcl_TraceTypeObjCmd TraceCommandObjCmd;
static Tcl_TraceTypeObjCmd TraceExecutionObjCmd;
static int TraceAddObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static int TraceRemoveObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
static int TraceInfoObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, Tcl_Obj *const objv[]);
/*
* Each primary subcommand has its own command procedure and its own index.
* The index is used to select the correct action.
*/
enum traceOptions {
TRACE_ADD, TRACE_INFO, TRACE_REMOVE
};
/*
* Each subcommand has a number of 'types' to which it can apply. Currently
* 'execution', 'command' and 'variable' are the only types supported. These
* three arrays MUST be kept in sync! In the future we may provide an API to
* add to the list of supported trace types.
*/
static const char *const traceTypeOptions[] = {
"execution", "command", "variable", NULL
};
@@ -163,6 +174,191 @@ typedef struct {
while ((clientData = Tcl_CommandTraceInfo(interp, name, 0, \
TraceCommandProc, clientData)) != NULL)
/*
*----------------------------------------------------------------------
*
* TclTraceInit --
*
* Registers the 'trace' subcommands with the Tcl interpreter
*
* Results:
* None.
*
* Side effects:
* Installs the commands (there are no client data)
*
* Note:
* Copied directly from the clock command's initialisation routine
*----------------------------------------------------------------------
*/
void
TclTraceInit(
Tcl_Interp *interp) /* Tcl interpreter */
{
char cmdName[50]; /* Buffer large enough to hold the string
*::tcl::trace::remove
* plus a terminating NUL. */
EnsembleImplMap *traceCmdPtr;
/* Structure of the 'trace' ensemble */
static EnsembleImplMap traceImplMap[] = {
{"add", TraceAddObjCmd, NULL, NULL, NULL, 0},
{"remove", TraceRemoveObjCmd, NULL, NULL, NULL, 0},
{"info", TraceInfoObjCmd, NULL, NULL, NULL, 0},
{NULL, NULL, NULL, NULL, NULL, 0}
};
/*
* Install the commands.
* TODO - Let Tcl_MakeEnsemble do this?
*/
#define TCL_TRACE_PREFIX_LEN 14 /* == strlen("::tcl::clock::") */
memcpy(cmdName, "::tcl::trace::", TCL_TRACE_PREFIX_LEN);
for (traceCmdPtr=traceImplMap ; traceCmdPtr->name!=NULL ; traceCmdPtr++) {
strcpy(cmdName + TCL_TRACE_PREFIX_LEN, traceCmdPtr->name);
Tcl_CreateObjCommand(interp, cmdName, traceCmdPtr->proc, NULL,
NULL);
}
/* Make the trace ensemble */
TclMakeEnsemble(interp, "trace", traceImplMap);
}
/*
*----------------------------------------------------------------------
*
* TraceAddObjCmd --
*
* This function is invoked to process the "trace add" Tcl command. See the
* user documentation for details on what it does.
*
* Standard syntax as of Tcl 8.4 is:
* trace add {execution|command|variable} name ops cmd
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static int
TraceAddObjCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
int typeIndex;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 1, objv, "name opList cmdPrefix");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], traceTypeOptions, "option",
0, &typeIndex) != TCL_OK) {
return TCL_ERROR;
}
return traceSubCmds[typeIndex](interp, TRACE_ADD, objc, objv);
}
/*
*----------------------------------------------------------------------
*
* TraceRemoveObjCmd --
*
* This function is invoked to process the "trace remove" Tcl command. See the
* user documentation for details on what it does.
*
* Standard syntax as of Tcl 8.4 is:
* trace remove {execution|command|variable} name ops cmd
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static int
TraceRemoveObjCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
int typeIndex;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name opList cmdPrefix");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], traceTypeOptions, "option",
0, &typeIndex) != TCL_OK) {
return TCL_ERROR;
}
return traceSubCmds[typeIndex](interp, TRACE_REMOVE, objc, objv);
}
/*
*----------------------------------------------------------------------
*
* TraceInfoObjCmd --
*
* This function is invoked to process the "trace info" Tcl command. See the
* user documentation for details on what it does.
*
* Standard syntax as of Tcl 8.4 is:
* trace remove {execution|command|variable} name
*
* Results:
* A standard Tcl result.
*
* Side effects:
* See the user documentation.
*----------------------------------------------------------------------
*/
/* ARGSUSED */
static int
TraceInfoObjCmd(
ClientData dummy, /* Not used. */
Tcl_Interp *interp, /* Current interpreter. */
int objc, /* Number of arguments. */
Tcl_Obj *const objv[]) /* Argument objects. */
{
int typeIndex;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "name");
return TCL_ERROR;
}
if (Tcl_GetIndexFromObj(interp, objv[1], traceTypeOptions, "option",
0, &typeIndex) != TCL_OK) {
return TCL_ERROR;
}
return traceSubCmds[typeIndex](interp, TRACE_INFO, objc, objv);
}
#ifndef TCL_ORIGINAL_TRACE
/*
*----------------------------------------------------------------------
*
@@ -191,24 +387,14 @@ Tcl_TraceObjCmd(
Tcl_Obj *const objv[]) /* Argument objects. */
{
int optionIndex;
#ifndef TCL_REMOVE_OBSOLETE_TRACES
const char *name;
const char *flagOps, *p;
#endif
/* Main sub commands to 'trace' */
static const char *const traceOptions[] = {
"add", "info", "remove",
#ifndef TCL_REMOVE_OBSOLETE_TRACES
"variable", "vdelete", "vinfo",
#endif
NULL
};
/* 'OLD' options are pre-Tcl-8.4 style */
enum traceOptions {
TRACE_ADD, TRACE_INFO, TRACE_REMOVE,
#ifndef TCL_REMOVE_OBSOLETE_TRACES
TRACE_OLD_VARIABLE, TRACE_OLD_VDELETE, TRACE_OLD_VINFO
#endif
};
if (objc < 2) {
@@ -266,116 +452,10 @@ Tcl_TraceObjCmd(
break;
}
#ifndef TCL_REMOVE_OBSOLETE_TRACES
case TRACE_OLD_VARIABLE:
case TRACE_OLD_VDELETE: {
Tcl_Obj *copyObjv[6];
Tcl_Obj *opsList;
int code, numFlags;
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name ops command");
return TCL_ERROR;
}
opsList = Tcl_NewObj();
Tcl_IncrRefCount(opsList);
flagOps = TclGetStringFromObj(objv[3], &numFlags);
if (numFlags == 0) {
Tcl_DecrRefCount(opsList);
goto badVarOps;
}
for (p = flagOps; *p != 0; p++) {
Tcl_Obj *opObj;
if (*p == 'r') {
TclNewLiteralStringObj(opObj, "read");
} else if (*p == 'w') {
TclNewLiteralStringObj(opObj, "write");
} else if (*p == 'u') {
TclNewLiteralStringObj(opObj, "unset");
} else if (*p == 'a') {
TclNewLiteralStringObj(opObj, "array");
} else {
Tcl_DecrRefCount(opsList);
goto badVarOps;
}
Tcl_ListObjAppendElement(NULL, opsList, opObj);
}
copyObjv[0] = NULL;
memcpy(copyObjv+1, objv, objc*sizeof(Tcl_Obj *));
copyObjv[4] = opsList;
if (optionIndex == TRACE_OLD_VARIABLE) {
code = traceSubCmds[2](interp, TRACE_ADD, objc+1, copyObjv);
} else {
code = traceSubCmds[2](interp, TRACE_REMOVE, objc+1, copyObjv);
}
Tcl_DecrRefCount(opsList);
return code;
}
case TRACE_OLD_VINFO: {
ClientData clientData;
char ops[5];
Tcl_Obj *resultListPtr, *pairObjPtr, *elemObjPtr;
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name");
return TCL_ERROR;
}
resultListPtr = Tcl_NewObj();
name = Tcl_GetString(objv[2]);
FOREACH_VAR_TRACE(interp, name, clientData) {
TraceVarInfo *tvarPtr = clientData;
char *q = ops;
pairObjPtr = Tcl_NewListObj(0, NULL);
if (tvarPtr->flags & TCL_TRACE_READS) {
*q = 'r';
q++;
}
if (tvarPtr->flags & TCL_TRACE_WRITES) {
*q = 'w';
q++;
}
if (tvarPtr->flags & TCL_TRACE_UNSETS) {
*q = 'u';
q++;
}
if (tvarPtr->flags & TCL_TRACE_ARRAY) {
*q = 'a';
q++;
}
*q = '\0';
/*
* Build a pair (2-item list) with the ops string as the first obj
* element and the tvarPtr->command string as the second obj
* element. Append the pair (as an element) to the end of the
* result object list.
*/
elemObjPtr = Tcl_NewStringObj(ops, -1);
Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
elemObjPtr = Tcl_NewStringObj(tvarPtr->command, -1);
Tcl_ListObjAppendElement(NULL, pairObjPtr, elemObjPtr);
Tcl_ListObjAppendElement(interp, resultListPtr, pairObjPtr);
}
Tcl_SetObjResult(interp, resultListPtr);
break;
}
#endif /* TCL_REMOVE_OBSOLETE_TRACES */
}
return TCL_OK;
#ifndef TCL_REMOVE_OBSOLETE_TRACES
badVarOps:
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"bad operations \"%s\": should be one or more of rwua",
flagOps));
Tcl_SetErrorCode(interp, "TCL", "OPERATION", "TRACE", "BADOPS", NULL);
return TCL_ERROR;
#endif
}
#endif
/*
*----------------------------------------------------------------------
@@ -424,8 +504,8 @@ TraceExecutionObjCmd(
int i, listLen, result;
Tcl_Obj **elemPtrs;
if (objc != 6) {
Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name opList command");
return TCL_ERROR;
}
@@ -434,7 +514,7 @@ TraceExecutionObjCmd(
* pointer to its array of element pointers.
*/
result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
result = Tcl_ListObjGetElements(interp, objv[3], &listLen, &elemPtrs);
if (result != TCL_OK) {
return result;
}
@@ -466,7 +546,7 @@ TraceExecutionObjCmd(
break;
}
}
command = TclGetStringFromObj(objv[5], &commandLength);
command = TclGetStringFromObj(objv[4], &commandLength);
length = (size_t) commandLength;
if ((enum traceOptions) optionIndex == TRACE_ADD) {
TraceCommandInfo *tcmdPtr = ckalloc(
@@ -484,7 +564,7 @@ TraceExecutionObjCmd(
flags |= (TCL_TRACE_ENTER_EXEC | TCL_TRACE_LEAVE_EXEC);
}
memcpy(tcmdPtr->command, command, length+1);
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
tcmdPtr) != TCL_OK) {
ckfree(tcmdPtr);
@@ -503,7 +583,7 @@ TraceExecutionObjCmd(
* First ensure the name given is valid.
*/
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
if (Tcl_FindCommand(interp,name,NULL,TCL_LEAVE_ERR_MSG) == NULL) {
return TCL_ERROR;
}
@@ -561,12 +641,12 @@ TraceExecutionObjCmd(
ClientData clientData;
Tcl_Obj *resultListPtr;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name");
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name");
return TCL_ERROR;
}
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
/*
* First ensure the name given is valid.
@@ -667,8 +747,8 @@ TraceCommandObjCmd(
int i, listLen, result;
Tcl_Obj **elemPtrs;
if (objc != 6) {
Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name opList command");
return TCL_ERROR;
}
@@ -677,7 +757,7 @@ TraceCommandObjCmd(
* pointer to its array of element pointers.
*/
result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
result = Tcl_ListObjGetElements(interp, objv[3], &listLen, &elemPtrs);
if (result != TCL_OK) {
return result;
}
@@ -705,7 +785,7 @@ TraceCommandObjCmd(
}
}
command = TclGetStringFromObj(objv[5], &commandLength);
command = TclGetStringFromObj(objv[4], &commandLength);
length = (size_t) commandLength;
if ((enum traceOptions) optionIndex == TRACE_ADD) {
TraceCommandInfo *tcmdPtr = ckalloc(
@@ -719,7 +799,7 @@ TraceCommandObjCmd(
tcmdPtr->refCount = 1;
flags |= TCL_TRACE_DELETE;
memcpy(tcmdPtr->command, command, length+1);
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
if (Tcl_TraceCommand(interp, name, flags, TraceCommandProc,
tcmdPtr) != TCL_OK) {
ckfree(tcmdPtr);
@@ -738,7 +818,7 @@ TraceCommandObjCmd(
* First ensure the name given is valid.
*/
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
if (Tcl_FindCommand(interp,name,NULL,TCL_LEAVE_ERR_MSG) == NULL) {
return TCL_ERROR;
}
@@ -765,8 +845,8 @@ TraceCommandObjCmd(
ClientData clientData;
Tcl_Obj *resultListPtr;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name");
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name");
return TCL_ERROR;
}
@@ -774,7 +854,7 @@ TraceCommandObjCmd(
* First ensure the name given is valid.
*/
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
if (Tcl_FindCommand(interp, name, NULL, TCL_LEAVE_ERR_MSG) == NULL) {
return TCL_ERROR;
}
@@ -866,8 +946,8 @@ TraceVariableObjCmd(
int i, listLen, result;
Tcl_Obj **elemPtrs;
if (objc != 6) {
Tcl_WrongNumArgs(interp, 3, objv, "name opList command");
if (objc != 5) {
Tcl_WrongNumArgs(interp, 2, objv, "name opList command");
return TCL_ERROR;
}
@@ -876,7 +956,7 @@ TraceVariableObjCmd(
* pointer to its array of element pointers.
*/
result = Tcl_ListObjGetElements(interp, objv[4], &listLen, &elemPtrs);
result = Tcl_ListObjGetElements(interp, objv[3], &listLen, &elemPtrs);
if (result != TCL_OK) {
return result;
}
@@ -908,7 +988,7 @@ TraceVariableObjCmd(
break;
}
}
command = TclGetStringFromObj(objv[5], &commandLength);
command = TclGetStringFromObj(objv[4], &commandLength);
length = (size_t) commandLength;
if ((enum traceOptions) optionIndex == TRACE_ADD) {
CombinedTraceVarInfo *ctvarPtr = ckalloc(
@@ -916,18 +996,13 @@ TraceVariableObjCmd(
+ 1 + length);
ctvarPtr->traceCmdInfo.flags = flags;
#ifndef TCL_REMOVE_OBSOLETE_TRACES
if (objv[0] == NULL) {
ctvarPtr->traceCmdInfo.flags |= TCL_TRACE_OLD_STYLE;
}
#endif
ctvarPtr->traceCmdInfo.length = length;
flags |= TCL_TRACE_UNSETS | TCL_TRACE_RESULT_OBJECT;
memcpy(ctvarPtr->traceCmdInfo.command, command, length+1);
ctvarPtr->traceInfo.traceProc = TraceVarProc;
ctvarPtr->traceInfo.clientData = &ctvarPtr->traceCmdInfo;
ctvarPtr->traceInfo.flags = flags;
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
if (TraceVarEx(interp, name, NULL, (VarTrace *) ctvarPtr)
!= TCL_OK) {
ckfree(ctvarPtr);
@@ -940,15 +1015,12 @@ TraceVariableObjCmd(
* first one that matches.
*/
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
FOREACH_VAR_TRACE(interp, name, clientData) {
TraceVarInfo *tvarPtr = clientData;
if ((tvarPtr->length == length)
&& ((tvarPtr->flags
#ifndef TCL_REMOVE_OBSOLETE_TRACES
& ~TCL_TRACE_OLD_STYLE
#endif
)==flags)
&& (strncmp(command, tvarPtr->command,
(size_t) length) == 0)) {
@@ -964,13 +1036,13 @@ TraceVariableObjCmd(
case TRACE_INFO: {
Tcl_Obj *resultListPtr;
if (objc != 4) {
Tcl_WrongNumArgs(interp, 3, objv, "name");
if (objc != 3) {
Tcl_WrongNumArgs(interp, 2, objv, "name");
return TCL_ERROR;
}
resultListPtr = Tcl_NewObj();
name = Tcl_GetString(objv[3]);
name = Tcl_GetString(objv[2]);
FOREACH_VAR_TRACE(interp, name, clientData) {
Tcl_Obj *opObjPtr, *eachTraceObjPtr, *elemObjPtr;
TraceVarInfo *tvarPtr = clientData;
@@ -2009,19 +2081,6 @@ TraceVarProc(
Tcl_DStringAppend(&cmd, tvarPtr->command, (int) tvarPtr->length);
Tcl_DStringAppendElement(&cmd, name1);
Tcl_DStringAppendElement(&cmd, (name2 ? name2 : ""));
#ifndef TCL_REMOVE_OBSOLETE_TRACES
if (tvarPtr->flags & TCL_TRACE_OLD_STYLE) {
if (flags & TCL_TRACE_ARRAY) {
TclDStringAppendLiteral(&cmd, " a");
} else if (flags & TCL_TRACE_READS) {
TclDStringAppendLiteral(&cmd, " r");
} else if (flags & TCL_TRACE_WRITES) {
TclDStringAppendLiteral(&cmd, " w");
} else if (flags & TCL_TRACE_UNSETS) {
TclDStringAppendLiteral(&cmd, " u");
}
} else {
#endif
if (flags & TCL_TRACE_ARRAY) {
TclDStringAppendLiteral(&cmd, " array");
} else if (flags & TCL_TRACE_READS) {
@@ -2031,9 +2090,6 @@ TraceVarProc(
} else if (flags & TCL_TRACE_UNSETS) {
TclDStringAppendLiteral(&cmd, " unset");
}
#ifndef TCL_REMOVE_OBSOLETE_TRACES
}
#endif
/*
* Execute the command. We discard any object result the command
@@ -2946,9 +3002,6 @@ Tcl_UntraceVar2(
flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
#ifndef TCL_REMOVE_OBSOLETE_TRACES
flagMask |= TCL_TRACE_OLD_STYLE;
#endif
flags &= flagMask;
hPtr = Tcl_FindHashEntry(&iPtr->varTraces, (char *) varPtr);
@@ -3297,9 +3350,6 @@ TraceVarEx(
flagMask = TCL_TRACE_READS | TCL_TRACE_WRITES | TCL_TRACE_UNSETS |
TCL_TRACE_ARRAY | TCL_TRACE_RESULT_DYNAMIC | TCL_TRACE_RESULT_OBJECT;
#ifndef TCL_REMOVE_OBSOLETE_TRACES
flagMask |= TCL_TRACE_OLD_STYLE;
#endif
tracePtr->flags = tracePtr->flags & flagMask;
hPtr = Tcl_CreateHashEntry(&iPtr->varTraces, varPtr, &isNew);
+8 -8
View File
@@ -188,7 +188,7 @@ test append-7.1 {lappend-created var and error in trace on that var} -setup {
catch {rename foo ""}
unset -nocomplain x
} -body {
trace variable x w foo
trace add variable x write foo
proc foo {} {global x; unset x}
catch {lappend x 1}
proc foo {args} {global x; unset x}
@@ -201,37 +201,37 @@ test append-7.2 {lappend var triggers read trace} -setup {
unset -nocomplain myvar
unset -nocomplain ::result
} -body {
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar a
return $::result
} -result {myvar {} r}
} -result {myvar {} read}
test append-7.3 {lappend var triggers read trace, array var} -setup {
unset -nocomplain myvar
unset -nocomplain ::result
} -body {
# The behavior of read triggers on lappend changed in 8.0 to not trigger
# them, and was changed back in 8.4.
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar(b) a
return $::result
} -result {myvar b r}
} -result {myvar b read}
test append-7.4 {lappend var triggers read trace, array var exists} -setup {
unset -nocomplain myvar
unset -nocomplain ::result
} -body {
set myvar(0) 1
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar(b) a
return $::result
} -result {myvar b r}
} -result {myvar b read}
test append-7.5 {append var does not trigger read trace} -setup {
unset -nocomplain myvar
unset -nocomplain ::result
} -body {
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
append myvar a
info exists ::result
+14 -14
View File
@@ -250,7 +250,7 @@ test appendComp-7.1 {lappendComp-created var and error in trace on that var} -se
} -body {
proc bar {} {
global x
trace variable x w foo
trace add variable x write foo
proc foo {} {global x; unset x}
catch {lappend x 1}
proc foo {args} {global x; unset x}
@@ -265,7 +265,7 @@ test appendComp-7.2 {lappend var triggers read trace, index var} -setup {
unset -nocomplain ::result
} -body {
proc bar {} {
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar a
return $::result
@@ -277,7 +277,7 @@ test appendComp-7.3 {lappend var triggers read trace, stack var} -setup {
unset -nocomplain ::myvar
} -body {
proc bar {} {
trace variable ::myvar r foo
trace add variable ::myvar read foo
proc foo {args} {append ::result $args}
lappend ::myvar a
return $::result
@@ -290,67 +290,67 @@ test appendComp-7.4 {lappend var triggers read trace, array var} -setup {
# The behavior of read triggers on lappend changed in 8.0 to not trigger
# them. Maybe not correct, but been there a while.
proc bar {} {
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar(b) a
return $::result
}
bar
} -result {myvar b r} -constraints {bug-3057639}
} -result {myvar b read} -constraints {bug-3057639}
test appendComp-7.5 {lappend var triggers read trace, array var} -setup {
unset -nocomplain ::result
} -body {
# The behavior of read triggers on lappend changed in 8.0 to not trigger
# them. Maybe not correct, but been there a while.
proc bar {} {
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar(b) a b
return $::result
}
bar
} -result {myvar b r}
} -result {myvar b read}
test appendComp-7.6 {lappend var triggers read trace, array var exists} -setup {
unset -nocomplain ::result
} -body {
proc bar {} {
set myvar(0) 1
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
lappend myvar(b) a
return $::result
}
bar
} -result {myvar b r} -constraints {bug-3057639}
} -result {myvar b read} -constraints {bug-3057639}
test appendComp-7.7 {lappend var triggers read trace, array stack var} -setup {
unset -nocomplain ::myvar
unset -nocomplain ::result
} -body {
proc bar {} {
trace variable ::myvar r foo
trace add variable ::myvar read foo
proc foo {args} {append ::result $args}
lappend ::myvar(b) a
return $::result
}
bar
} -result {::myvar b r} -constraints {bug-3057639}
} -result {::myvar b read} -constraints {bug-3057639}
test appendComp-7.8 {lappend var triggers read trace, array stack var} -setup {
unset -nocomplain ::myvar
unset -nocomplain ::result
} -body {
proc bar {} {
trace variable ::myvar r foo
trace add variable ::myvar read foo
proc foo {args} {append ::result $args}
lappend ::myvar(b) a b
return $::result
}
bar
} -result {::myvar b r}
} -result {::myvar b read}
test appendComp-7.9 {append var does not trigger read trace} -setup {
unset -nocomplain ::result
} -body {
proc bar {} {
trace variable myvar r foo
trace add variable myvar read foo
proc foo {args} {append ::result $args}
append myvar a
info exists ::result
+1 -1
View File
@@ -644,7 +644,7 @@ test binary-18.1 {Tcl_BinaryObjCmd: format} -returnCodes error -body {
test binary-19.1 {Tcl_BinaryObjCmd: errors} -returnCodes error -body {
binary s
} -result {wrong # args: should be "binary scan value formatString ?varName ...?"}
} -result {unknown or ambiguous subcommand "s": must be decode, encode, format, scan, or set}
test binary-19.2 {Tcl_BinaryObjCmd: errors} -returnCodes error -body {
binary scan foo
} -result {wrong # args: should be "binary scan value formatString ?varName ...?"}
+1 -1
View File
@@ -763,7 +763,7 @@ test expr-20.2 {double invocation of variable traces} -body {
set var "$counter + [concat $extraargs]"
}
}
trace variable exprtracevar r [list exprtraceproc 10]
trace add variable exprtracevar read [list exprtraceproc 10]
list [catch {expr "$exprtracevar + 20"} a] $a \
[catch {expr "$exprtracevar + 20"} b] $b \
[unset exprtracevar exprtracecounter]
+1 -1
View File
@@ -1265,7 +1265,7 @@ test if-10.6 {double invocation of variable traces} -body {
set var "$counter + [concat $extraargs]"
}
}
trace variable iftracevar r [list iftraceproc 10]
trace add variable iftracevar read [list iftraceproc 10]
list [catch {if "$iftracevar + 20" {}} a] $a \
[catch {if "$iftracevar + 20" {}} b] $b
} -cleanup {
+1 -1
View File
@@ -63,7 +63,7 @@ test incr-old-2.5 {incr errors} {
test incr-old-2.6 {incr errors} -body {
proc readonly args {error "variable is read-only"}
set x 123
trace var x w readonly
trace add variable x write readonly
list [catch {incr x 1} msg] $msg $::errorInfo
} -match glob -result {1 {can't set "x": variable is read-only} {*variable is read-only
while executing
+1 -1
View File
@@ -157,7 +157,7 @@ foreach arg [subst -nocommands -novariables {
auto_reset
} -body {
namespace eval junk [list array set $arg [list 1 2 3 4]]
trace variable ::junk::$arg r \
trace add variable ::junk::$arg read \
"[list error [subst {Variable \"$arg\" is write-only}]] ;# "
catch {parray ::junk::$arg}
set first $::errorInfo
+4 -4
View File
@@ -373,9 +373,9 @@ test link-8.1 {Tcl_UpdateLinkedVar procedure} {testlink} {
set x {}
testlink create 1 1 1 1 1 1 1 1 1 1 1 1 1 1
testlink set 14 -2.0 0 xyzzy 995511 64 250 30000 60000 0xbeefbabe 12321 32123 3.25 1231231234
trace var int w x
trace add variable int write x
testlink update 32 4.0 3 abcd 113355 65 251 30001 60001 0xbabebeef 12322 32124 3.125 12312312340
trace vdelete int w x
trace remove variable int write x
return $x
} {{int {} w} 32 -2.0 0 xyzzy 995511}
test link-8.2 {Tcl_UpdateLinkedVar procedure} {testlink} {
@@ -387,9 +387,9 @@ test link-8.2 {Tcl_UpdateLinkedVar procedure} {testlink} {
testlink create 1 1 1 1 1 1 1 1 1 1 1 1 1 1
testlink set 14 -2.0 0 xyzzy 995511 64 250 30000 60000 0xbeefbabe 12321 32123 3.25 1231231234
testlink delete
trace var int w x
trace add variable int write x
testlink update 32 4.0 6 abcd 113355 65 251 30001 60001 0xbabebeef 12322 32124 3.125 12312312340
trace vdelete int w x
trace remove variable int write x
return $x
} {}
test link-8.3 {Tcl_UpdateLinkedVar procedure, read-only variable} {testlink} {
+2 -2
View File
@@ -635,13 +635,13 @@ test namespace-old-8.1 {traces work across namespace boundaries} {
variable status
lappend status "$op: $name1"
}
trace variable foo::x rwu [namespace code monitor]
trace add variable foo::x {read write unset} [namespace code monitor]
}
set test_ns_trace::foo::x "yes!"
set test_ns_trace::foo::x
unset test_ns_trace::foo::x
namespace eval test_ns_trace { set status }
} {{w: test_ns_trace::foo::x} {r: test_ns_trace::foo::x} {u: test_ns_trace::foo::x}}
} {{write: test_ns_trace::foo::x} {read: test_ns_trace::foo::x} {unset: test_ns_trace::foo::x}}
# -----------------------------------------------------------------------
# TEST: imported commands
+8 -8
View File
@@ -137,25 +137,25 @@ test proc-old-3.7 {local and global arrays} {
proc t1 {args} {global info; set info 1}
catch {unset a}
set info {}
do {global a; trace var a(1) w t1}
do {global a; trace add variable a(1) write t1}
set a(1) 44
set info
} 1
test proc-old-3.8 {local and global arrays} {
proc t1 {args} {global info; set info 1}
catch {unset a}
trace var a(1) w t1
trace add variable a(1) write t1
set info {}
do {global a; trace vdelete a(1) w t1}
do {global a; trace remove variable a(1) write t1}
set a(1) 44
set info
} {}
test proc-old-3.9 {local and global arrays} {
proc t1 {args} {global info; set info 1}
catch {unset a}
trace var a(1) w t1
do {global a; trace vinfo a(1)}
} {{w t1}}
trace add variable a(1) write t1
do {global a; trace info variable a(1)}
} {{write t1}}
catch {unset a}
test proc-old-30.1 {arguments and defaults} {
@@ -349,7 +349,7 @@ test proc-old-5.16 {error conditions} {
}
proc tproc {} {
set x 44
trace var x u foo
trace add variable x unset foo
while {$x < 100} {
error "Nested error"
}
@@ -361,7 +361,7 @@ test proc-old-5.16 {error conditions} {
"error "Nested error""
(procedure "tproc" line 5)
invoked from within
"tproc"} {foo was called: x {} u}}
"tproc"} {foo was called: x {} unset}}
# The tests below will really only be useful when run under Purify or
# some other system that can detect accesses to freed memory...
+8 -8
View File
@@ -169,7 +169,7 @@ test set-old-5.4 {errors in reading variables} {
test set-old-6.1 {creating array during write} {
catch {unset a}
trace var a rwu ignore
trace add variable a {read write unset} ignore
list [catch {set a(14) 186} msg] $msg [array names a]
} {0 186 14}
test set-old-6.2 {errors in writing variables} {
@@ -407,7 +407,7 @@ test set-old-8.18 {array command, get option} {
test set-old-8.19 {array command, get option (unset variable)} {
catch {unset a}
set a(x) 3
trace var a(y) w ignore
trace add variable a(y) write ignore
array get a
} {x 3}
test set-old-8.20 {array command, get option, with pattern} {
@@ -445,13 +445,13 @@ test set-old-8.24 {array command, names option} {
test set-old-8.25 {array command, names option} {
catch {unset a}
set a(22) 3; set a(33) 44;
trace var a(xxx) w ignore
trace add variable a(xxx) write ignore
list [catch {lsort [array names a]} msg] $msg
} {0 {22 33}}
test set-old-8.26 {array command, names option} {
catch {unset a}
set a(22) 3; set a(33) 44;
trace var a(xxx) w ignore
trace add variable a(xxx) write ignore
set a(xxx) value
list [catch {lsort [array names a]} msg] $msg
} {0 {22 33 xxx}}
@@ -579,7 +579,7 @@ test set-old-8.43 {array command, size option} {
test set-old-8.44 {array command, size option} {
catch {unset a}
set a(22) 3;
trace var a(33) rwu ignore
trace add variable a(33) {read write unset} ignore
list [catch {array size a} msg] $msg
} {0 1}
test set-old-8.45 {array command, size option, array doesn't exist yet but has compiler-allocated procedure slot} {
@@ -786,7 +786,7 @@ test set-old-9.10 {array enumeration: searches automatically stopped} {
set a(a) 1
set x [array startsearch a]
set y [array startsearch a]
trace var a(b) r {}
trace add variable a(b) read {}
list [catch {array next a $x} msg] $msg \
[catch {array next a $y} msg2] $msg2
} {1 {couldn't find search "s-1-a"} 1 {couldn't find search "s-2-a"}}
@@ -795,14 +795,14 @@ test set-old-9.11 {array enumeration: searches automatically stopped} {
set a(a) 1
set x [array startsearch a]
set y [array startsearch a]
trace var a(a) r {}
trace add variable a(a) read {}
list [catch {array next a $x} msg] $msg \
[catch {array next a $y} msg2] $msg2
} {0 a 0 a}
test set-old-9.12 {array enumeration with traced undefined elements} {
catch {unset a}
set a(a) 1
trace var a(b) r {}
trace add variable a(b) read {}
set x [array startsearch a]
lsort [list [array next a $x] [array next a $x]]
} {{} a}
+2 -2
View File
@@ -263,7 +263,7 @@ test set-2.4 {set command: runtime error, readonly variable} -setup {
} -body {
proc readonly args {error "variable is read-only"}
set x 123
trace var x w readonly
trace add variable x write readonly
list [catch {set x 1} msg] $msg $::errorInfo
} -match glob -result {1 {can't set "x": variable is read-only} {*variable is read-only
while executing
@@ -521,7 +521,7 @@ test set-4.4 {uncompiled set command: runtime error, readonly variable} -body {
set z set
proc readonly args {error "variable is read-only"}
$z x 123
trace var x w readonly
trace add variable x write readonly
list [catch {$z x 1} msg] $msg $::errorInfo
} -match glob -result {1 {can't set "x": variable is read-only} {*variable is read-only
while executing
+7 -7
View File
@@ -187,26 +187,26 @@ test upvar-4.2 {nested upvars} {
proc tproc {args} {global x; set x [list $args [uplevel info vars]]}
test upvar-5.1 {traces involving upvars} {
proc p1 {a b} {set c 22; set d 33; trace var c rw tproc; p2}
proc p1 {a b} {set c 22; set d 33; trace add variable c {read write} tproc; p2}
proc p2 {} {upvar c x1; set x1 22}
set x ---
p1 foo bar
set x
} {{x1 {} w} x1}
} {{x1 {} write} x1}
test upvar-5.2 {traces involving upvars} {
proc p1 {a b} {set c 22; set d 33; trace var c rw tproc; p2}
proc p1 {a b} {set c 22; set d 33; trace add variable c {read write} tproc; p2}
proc p2 {} {upvar c x1; set x1}
set x ---
p1 foo bar
set x
} {{x1 {} r} x1}
} {{x1 {} read} x1}
test upvar-5.3 {traces involving upvars} {
proc p1 {a b} {set c 22; set d 33; trace var c rwu tproc; p2}
proc p1 {a b} {set c 22; set d 33; trace add variable c {read write unset} tproc; p2}
proc p2 {} {upvar c x1; unset x1}
set x ---
p1 foo bar
set x
} {{x1 {} u} x1}
} {{x1 {} unset} x1}
test upvar-6.1 {retargeting an upvar} {
proc p1 {} {
@@ -317,7 +317,7 @@ test upvar-8.6 {errors in upvar command} -returnCodes error -body {
p1
} -result {variable "a" already exists}
test upvar-8.7 {errors in upvar command} -returnCodes error -body {
proc p1 {} {trace variable a w foo; upvar b a}
proc p1 {} {trace add variable a write foo; upvar b a}
p1
} -result {variable "a" has traces: can't use for upvar}
test upvar-8.8 {create nested array with upvar} -body {
+12 -12
View File
@@ -576,10 +576,10 @@ test var-8.1 {TclDeleteVars, "unset" traces are called with fully-qualified var
variable info
set info [concat $info [list $name1 $name2 $op]]
}
trace var v u [namespace code traceUnset]
trace add variable v unset [namespace code traceUnset]
}
list [unset test_ns_var::v] $test_ns_var::info
} -result {{} {test_ns_var::v {} u}}
} -result {{} {test_ns_var::v {} unset}}
test var-8.2 {TclDeleteNamespaceVars, "unset" traces on ns delete are called with fully-qualified var names} -setup {
catch {namespace delete test_ns_var}
catch {unset a}
@@ -587,13 +587,13 @@ test var-8.2 {TclDeleteNamespaceVars, "unset" traces on ns delete are called wit
set info ""
namespace eval test_ns_var {
variable v 123 1
trace var v u ::traceUnset
trace add variable v unset ::traceUnset
}
proc traceUnset {name1 name2 op} {
set ::info [concat $::info [list $name1 $name2 $op]]
}
list [namespace delete test_ns_var] $::info
} -result {{} {::test_ns_var::v {} u}}
} -result {{} {::test_ns_var::v {} unset}}
test var-8.3 {TclDeleteNamespaceVars, mem leak} -constraints memory -setup {
proc ::t {a i o} {
@@ -603,7 +603,7 @@ test var-8.3 {TclDeleteNamespaceVars, mem leak} -constraints memory -setup {
leaktest {
namespace eval n {
variable v 123
trace variable v u ::t
trace add variable v unset ::t
}
namespace delete n
}
@@ -682,8 +682,8 @@ test var-9.9 {behaviour of TclGetVar read trace success} -setup {
} -constraints testsetnoerr -body {
proc resetvar {val name elem op} {upvar 1 $name v; set v $val}
set u 10
trace var u r [list resetvar 1]
trace var v r [list resetvar 2]
trace add variable u read [list resetvar 1]
trace add variable v read [list resetvar 2]
list \
[testsetnoerr u] \
[testseterr v]
@@ -691,7 +691,7 @@ test var-9.9 {behaviour of TclGetVar read trace success} -setup {
test var-9.10 {behaviour of TclGetVar read trace error} testsetnoerr {
proc writeonly args {error "write-only"}
set v 456
trace var v r writeonly
trace add variable v read writeonly
list \
[catch {testsetnoerr v} msg] $msg \
[catch {testseterr v} msg] $msg
@@ -702,8 +702,8 @@ test var-9.11 {behaviour of TclSetVar write trace success} -setup {
} -constraints testsetnoerr -body {
proc doubleval {name elem op} {upvar 1 $name v; set v [expr {2 * $v}]}
set v 1
trace var v w doubleval
trace var u w doubleval
trace add variable v write doubleval
trace add variable u write doubleval
list \
[testsetnoerr u 2] \
[testseterr v 3]
@@ -711,7 +711,7 @@ test var-9.11 {behaviour of TclSetVar write trace success} -setup {
test var-9.12 {behaviour of TclSetVar write trace error} testsetnoerr {
proc readonly args {error "read-only"}
set v 456
trace var v w readonly
trace add variable v write readonly
list \
[catch {testsetnoerr v 2} msg] $msg $v \
[catch {testseterr v 3} msg] $msg $v
@@ -773,7 +773,7 @@ test var-13.1 {Tcl_UnsetVar2, unset array with trace set on element} -setup {
}
namespace eval :: {
set t(1) 1
trace variable t(1) u foo
trace add variable t(1) unset foo
unset t
}
set x "If you see this, it worked"