diff options
Diffstat (limited to 'generic/tclBasic.c')
-rw-r--r-- | generic/tclBasic.c | 7572 |
1 files changed, 3932 insertions, 3640 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 6e457c0..5e6b500 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -1,9 +1,9 @@ -/* +/* * tclBasic.c -- * * Contains the basic facilities for TCL command interpretation, - * including interpreter creation and deletion, command creation - * and deletion, and command/script execution. + * including interpreter creation and deletion, command creation and + * deletion, and command/script execution. * * Copyright (c) 1987-1994 The Regents of the University of California. * Copyright (c) 1994-1997 Sun Microsystems, Inc. @@ -11,261 +11,353 @@ * Copyright (c) 2001, 2002 by Kevin B. Kenny. All rights reserved. * Copyright (c) 2007 Daniel A. Steffen <das@users.sourceforge.net> * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + * See the file "license.terms" for information on usage and redistribution of + * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ -#if defined(_WIN32) && !defined(_WIN64) -# define _USE_32BIT_TIME_T -# include <sys/stat.h> -#endif - #include "tclInt.h" #include "tclCompile.h" -#ifndef TCL_GENERIC_ONLY -# include "tclPort.h" -#endif +#include <float.h> +#include <limits.h> +#include <math.h> +#include "tommath.h" /* - * Static procedures in this file: + * Determine whether we're using IEEE floating point */ -static char * CallCommandTraces _ANSI_ARGS_((Interp *iPtr, - Command *cmdPtr, CONST char *oldName, - CONST char* newName, int flags)); -static void DeleteInterpProc _ANSI_ARGS_((Tcl_Interp *interp)); -static void ProcessUnexpectedResult _ANSI_ARGS_(( - Tcl_Interp *interp, int returnCode)); -static int StringTraceProc _ANSI_ARGS_((ClientData clientData, - Tcl_Interp* interp, - int level, - CONST char* command, - Tcl_Command commandInfo, - int objc, - Tcl_Obj *CONST objv[])); -static void StringTraceDeleteProc _ANSI_ARGS_((ClientData clientData)); - -#ifdef TCL_TIP280 -/* TIP #280 - Modified token based evaluation, with line information */ -static int EvalEx _ANSI_ARGS_((Tcl_Interp *interp, CONST char *script, - int numBytes, int flags, int line, - int* clNextOuter, CONST char* outerScript)); - -static int EvalTokensStandard _ANSI_ARGS_((Tcl_Interp *interp, - Tcl_Token *tokenPtr, - int count, int line, - int* clNextOuter, CONST char* outerScript)); +#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024) +# define IEEE_FLOATING_POINT +/* Largest odd integer that can be represented exactly in a double */ +# define MAX_EXACT 9007199254740991.0 #endif +/* + * The following structure defines the client data for a math function + * registered with Tcl_CreateMathFunc + */ + +typedef struct OldMathFuncData { + Tcl_MathProc *proc; /* Handler function */ + int numArgs; /* Number of args expected */ + Tcl_ValueType *argTypes; /* Types of the args */ + ClientData clientData; /* Client data for the handler function */ +} OldMathFuncData; + +/* + * Static functions in this file: + */ + +static char * CallCommandTraces(Interp *iPtr, Command *cmdPtr, + const char *oldName, const char *newName, int flags); +static int CheckDoubleResult(Tcl_Interp *interp, double dResult); +static void DeleteInterpProc(Tcl_Interp *interp); +static void DeleteOpCmdClientData(ClientData clientData); +static Tcl_Obj *GetCommandSource(Interp *iPtr, const char *command, + int numChars, int objc, Tcl_Obj *const objv[]); +static void ProcessUnexpectedResult(Tcl_Interp *interp, int returnCode); +static int OldMathFuncProc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static void OldMathFuncDeleteProc(ClientData clientData); +static int ExprAbsFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprBinaryFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprBoolFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprCeilFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprDoubleFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprEntierFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprFloorFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprIntFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprIsqrtFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprRandFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprRoundFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprSqrtFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprSrandFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprUnaryFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static int ExprWideFunc(ClientData clientData, Tcl_Interp *interp, + int argc, Tcl_Obj *const *objv); +static void MathFuncWrongNumArgs(Tcl_Interp *interp, int expected, + int actual, Tcl_Obj *const *objv); #ifdef USE_DTRACE static int DTraceObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, - Tcl_Obj *CONST objv[]); + Tcl_Obj *const objv[]); #endif extern TclStubs tclStubs; /* - * The following structure defines the commands in the Tcl core. + * The following structure define the commands in the Tcl core. */ typedef struct { - CONST char *name; /* Name of object-based command. */ - Tcl_CmdProc *proc; /* String-based procedure for command. */ - Tcl_ObjCmdProc *objProc; /* Object-based procedure for command. */ - CompileProc *compileProc; /* Procedure called to compile command. */ - int isSafe; /* If non-zero, command will be present - * in safe interpreter. Otherwise it will - * be hidden. */ + const char *name; /* Name of object-based command. */ + Tcl_ObjCmdProc *objProc; /* Object-based function for command. */ + CompileProc *compileProc; /* Function called to compile command. */ + int isSafe; /* If non-zero, command will be present in + * safe interpreter. Otherwise it will be + * hidden. */ } CmdInfo; /* - * The built-in commands, and the procedures that implement them: + * The built-in commands, and the functions that implement them: */ -static CONST CmdInfo builtInCmds[] = { - /* - * Commands in the generic core. Note that at least one of the proc or - * objProc members should be non-NULL. This avoids infinitely recursive - * calls between TclInvokeObjectCommand and TclInvokeStringCommand if a - * command name is computed at runtime and results in the name of a - * compiled command. - */ - - {"append", (Tcl_CmdProc *) NULL, Tcl_AppendObjCmd, - TclCompileAppendCmd, 1}, - {"array", (Tcl_CmdProc *) NULL, Tcl_ArrayObjCmd, - (CompileProc *) NULL, 1}, - {"binary", (Tcl_CmdProc *) NULL, Tcl_BinaryObjCmd, - (CompileProc *) NULL, 1}, - {"break", (Tcl_CmdProc *) NULL, Tcl_BreakObjCmd, - TclCompileBreakCmd, 1}, - {"case", (Tcl_CmdProc *) NULL, Tcl_CaseObjCmd, - (CompileProc *) NULL, 1}, - {"catch", (Tcl_CmdProc *) NULL, Tcl_CatchObjCmd, - TclCompileCatchCmd, 1}, - {"clock", (Tcl_CmdProc *) NULL, Tcl_ClockObjCmd, - (CompileProc *) NULL, 1}, - {"concat", (Tcl_CmdProc *) NULL, Tcl_ConcatObjCmd, - (CompileProc *) NULL, 1}, - {"continue", (Tcl_CmdProc *) NULL, Tcl_ContinueObjCmd, - TclCompileContinueCmd, 1}, - {"encoding", (Tcl_CmdProc *) NULL, Tcl_EncodingObjCmd, - (CompileProc *) NULL, 0}, - {"error", (Tcl_CmdProc *) NULL, Tcl_ErrorObjCmd, - (CompileProc *) NULL, 1}, - {"eval", (Tcl_CmdProc *) NULL, Tcl_EvalObjCmd, - (CompileProc *) NULL, 1}, - {"exit", (Tcl_CmdProc *) NULL, Tcl_ExitObjCmd, - (CompileProc *) NULL, 0}, - {"expr", (Tcl_CmdProc *) NULL, Tcl_ExprObjCmd, - TclCompileExprCmd, 1}, - {"fcopy", (Tcl_CmdProc *) NULL, Tcl_FcopyObjCmd, - (CompileProc *) NULL, 1}, - {"fileevent", (Tcl_CmdProc *) NULL, Tcl_FileEventObjCmd, - (CompileProc *) NULL, 1}, - {"for", (Tcl_CmdProc *) NULL, Tcl_ForObjCmd, - TclCompileForCmd, 1}, - {"foreach", (Tcl_CmdProc *) NULL, Tcl_ForeachObjCmd, - TclCompileForeachCmd, 1}, - {"format", (Tcl_CmdProc *) NULL, Tcl_FormatObjCmd, - (CompileProc *) NULL, 1}, - {"global", (Tcl_CmdProc *) NULL, Tcl_GlobalObjCmd, - (CompileProc *) NULL, 1}, - {"if", (Tcl_CmdProc *) NULL, Tcl_IfObjCmd, - TclCompileIfCmd, 1}, - {"incr", (Tcl_CmdProc *) NULL, Tcl_IncrObjCmd, - TclCompileIncrCmd, 1}, - {"info", (Tcl_CmdProc *) NULL, Tcl_InfoObjCmd, - (CompileProc *) NULL, 1}, - {"join", (Tcl_CmdProc *) NULL, Tcl_JoinObjCmd, - (CompileProc *) NULL, 1}, - {"lappend", (Tcl_CmdProc *) NULL, Tcl_LappendObjCmd, - TclCompileLappendCmd, 1}, - {"lindex", (Tcl_CmdProc *) NULL, Tcl_LindexObjCmd, - TclCompileLindexCmd, 1}, - {"linsert", (Tcl_CmdProc *) NULL, Tcl_LinsertObjCmd, - (CompileProc *) NULL, 1}, - {"list", (Tcl_CmdProc *) NULL, Tcl_ListObjCmd, - TclCompileListCmd, 1}, - {"llength", (Tcl_CmdProc *) NULL, Tcl_LlengthObjCmd, - TclCompileLlengthCmd, 1}, - {"load", (Tcl_CmdProc *) NULL, Tcl_LoadObjCmd, - (CompileProc *) NULL, 0}, - {"lrange", (Tcl_CmdProc *) NULL, Tcl_LrangeObjCmd, - (CompileProc *) NULL, 1}, - {"lreplace", (Tcl_CmdProc *) NULL, Tcl_LreplaceObjCmd, - (CompileProc *) NULL, 1}, - {"lsearch", (Tcl_CmdProc *) NULL, Tcl_LsearchObjCmd, - (CompileProc *) NULL, 1}, - {"lset", (Tcl_CmdProc *) NULL, Tcl_LsetObjCmd, - TclCompileLsetCmd, 1}, - {"lsort", (Tcl_CmdProc *) NULL, Tcl_LsortObjCmd, - (CompileProc *) NULL, 1}, - {"namespace", (Tcl_CmdProc *) NULL, Tcl_NamespaceObjCmd, - (CompileProc *) NULL, 1}, - {"package", (Tcl_CmdProc *) NULL, Tcl_PackageObjCmd, - (CompileProc *) NULL, 1}, - {"proc", (Tcl_CmdProc *) NULL, Tcl_ProcObjCmd, - (CompileProc *) NULL, 1}, - {"regexp", (Tcl_CmdProc *) NULL, Tcl_RegexpObjCmd, - TclCompileRegexpCmd, 1}, - {"regsub", (Tcl_CmdProc *) NULL, Tcl_RegsubObjCmd, - (CompileProc *) NULL, 1}, - {"rename", (Tcl_CmdProc *) NULL, Tcl_RenameObjCmd, - (CompileProc *) NULL, 1}, - {"return", (Tcl_CmdProc *) NULL, Tcl_ReturnObjCmd, - TclCompileReturnCmd, 1}, - {"scan", (Tcl_CmdProc *) NULL, Tcl_ScanObjCmd, - (CompileProc *) NULL, 1}, - {"set", (Tcl_CmdProc *) NULL, Tcl_SetObjCmd, - TclCompileSetCmd, 1}, - {"split", (Tcl_CmdProc *) NULL, Tcl_SplitObjCmd, - (CompileProc *) NULL, 1}, - {"string", (Tcl_CmdProc *) NULL, Tcl_StringObjCmd, - TclCompileStringCmd, 1}, - {"subst", (Tcl_CmdProc *) NULL, Tcl_SubstObjCmd, - (CompileProc *) NULL, 1}, - {"switch", (Tcl_CmdProc *) NULL, Tcl_SwitchObjCmd, - (CompileProc *) NULL, 1}, - {"trace", (Tcl_CmdProc *) NULL, Tcl_TraceObjCmd, - (CompileProc *) NULL, 1}, - {"unset", (Tcl_CmdProc *) NULL, Tcl_UnsetObjCmd, - (CompileProc *) NULL, 1}, - {"uplevel", (Tcl_CmdProc *) NULL, Tcl_UplevelObjCmd, - (CompileProc *) NULL, 1}, - {"upvar", (Tcl_CmdProc *) NULL, Tcl_UpvarObjCmd, - (CompileProc *) NULL, 1}, - {"variable", (Tcl_CmdProc *) NULL, Tcl_VariableObjCmd, - (CompileProc *) NULL, 1}, - {"while", (Tcl_CmdProc *) NULL, Tcl_WhileObjCmd, - TclCompileWhileCmd, 1}, - - /* - * Commands in the UNIX core: - */ - -#ifndef TCL_GENERIC_ONLY - {"after", (Tcl_CmdProc *) NULL, Tcl_AfterObjCmd, - (CompileProc *) NULL, 1}, - {"cd", (Tcl_CmdProc *) NULL, Tcl_CdObjCmd, - (CompileProc *) NULL, 0}, - {"close", (Tcl_CmdProc *) NULL, Tcl_CloseObjCmd, - (CompileProc *) NULL, 1}, - {"eof", (Tcl_CmdProc *) NULL, Tcl_EofObjCmd, - (CompileProc *) NULL, 1}, - {"fblocked", (Tcl_CmdProc *) NULL, Tcl_FblockedObjCmd, - (CompileProc *) NULL, 1}, - {"fconfigure", (Tcl_CmdProc *) NULL, Tcl_FconfigureObjCmd, - (CompileProc *) NULL, 0}, - {"file", (Tcl_CmdProc *) NULL, Tcl_FileObjCmd, - (CompileProc *) NULL, 0}, - {"flush", (Tcl_CmdProc *) NULL, Tcl_FlushObjCmd, - (CompileProc *) NULL, 1}, - {"gets", (Tcl_CmdProc *) NULL, Tcl_GetsObjCmd, - (CompileProc *) NULL, 1}, - {"glob", (Tcl_CmdProc *) NULL, Tcl_GlobObjCmd, - (CompileProc *) NULL, 0}, - {"open", (Tcl_CmdProc *) NULL, Tcl_OpenObjCmd, - (CompileProc *) NULL, 0}, - {"pid", (Tcl_CmdProc *) NULL, Tcl_PidObjCmd, - (CompileProc *) NULL, 1}, - {"puts", (Tcl_CmdProc *) NULL, Tcl_PutsObjCmd, - (CompileProc *) NULL, 1}, - {"pwd", (Tcl_CmdProc *) NULL, Tcl_PwdObjCmd, - (CompileProc *) NULL, 0}, - {"read", (Tcl_CmdProc *) NULL, Tcl_ReadObjCmd, - (CompileProc *) NULL, 1}, - {"seek", (Tcl_CmdProc *) NULL, Tcl_SeekObjCmd, - (CompileProc *) NULL, 1}, - {"socket", (Tcl_CmdProc *) NULL, Tcl_SocketObjCmd, - (CompileProc *) NULL, 0}, - {"tell", (Tcl_CmdProc *) NULL, Tcl_TellObjCmd, - (CompileProc *) NULL, 1}, - {"time", (Tcl_CmdProc *) NULL, Tcl_TimeObjCmd, - (CompileProc *) NULL, 1}, - {"update", (Tcl_CmdProc *) NULL, Tcl_UpdateObjCmd, - (CompileProc *) NULL, 1}, - {"vwait", (Tcl_CmdProc *) NULL, Tcl_VwaitObjCmd, - (CompileProc *) NULL, 1}, - {"exec", (Tcl_CmdProc *) NULL, Tcl_ExecObjCmd, - (CompileProc *) NULL, 0}, - {"source", (Tcl_CmdProc *) NULL, Tcl_SourceObjCmd, - (CompileProc *) NULL, 0}, - -#endif /* TCL_GENERIC_ONLY */ - {NULL, (Tcl_CmdProc *) NULL, (Tcl_ObjCmdProc *) NULL, - (CompileProc *) NULL, 0} +static const CmdInfo builtInCmds[] = { + /* + * Commands in the generic core. + */ + + {"append", Tcl_AppendObjCmd, TclCompileAppendCmd, 1}, + {"apply", Tcl_ApplyObjCmd, NULL, 1}, + {"array", Tcl_ArrayObjCmd, NULL, 1}, + {"binary", Tcl_BinaryObjCmd, NULL, 1}, + {"break", Tcl_BreakObjCmd, TclCompileBreakCmd, 1}, +#ifndef EXCLUDE_OBSOLETE_COMMANDS + {"case", Tcl_CaseObjCmd, NULL, 1}, +#endif + {"catch", Tcl_CatchObjCmd, TclCompileCatchCmd, 1}, + {"concat", Tcl_ConcatObjCmd, NULL, 1}, + {"continue", Tcl_ContinueObjCmd, TclCompileContinueCmd, 1}, + {"error", Tcl_ErrorObjCmd, NULL, 1}, + {"eval", Tcl_EvalObjCmd, NULL, 1}, + {"expr", Tcl_ExprObjCmd, TclCompileExprCmd, 1}, + {"for", Tcl_ForObjCmd, TclCompileForCmd, 1}, + {"foreach", Tcl_ForeachObjCmd, TclCompileForeachCmd, 1}, + {"format", Tcl_FormatObjCmd, NULL, 1}, + {"global", Tcl_GlobalObjCmd, TclCompileGlobalCmd, 1}, + {"if", Tcl_IfObjCmd, TclCompileIfCmd, 1}, + {"incr", Tcl_IncrObjCmd, TclCompileIncrCmd, 1}, + {"join", Tcl_JoinObjCmd, NULL, 1}, + {"lappend", Tcl_LappendObjCmd, TclCompileLappendCmd, 1}, + {"lassign", Tcl_LassignObjCmd, TclCompileLassignCmd, 1}, + {"lindex", Tcl_LindexObjCmd, TclCompileLindexCmd, 1}, + {"linsert", Tcl_LinsertObjCmd, NULL, 1}, + {"list", Tcl_ListObjCmd, TclCompileListCmd, 1}, + {"llength", Tcl_LlengthObjCmd, TclCompileLlengthCmd, 1}, + {"lrange", Tcl_LrangeObjCmd, NULL, 1}, + {"lrepeat", Tcl_LrepeatObjCmd, NULL, 1}, + {"lreplace", Tcl_LreplaceObjCmd, NULL, 1}, + {"lreverse", Tcl_LreverseObjCmd, NULL, 1}, + {"lsearch", Tcl_LsearchObjCmd, NULL, 1}, + {"lset", Tcl_LsetObjCmd, TclCompileLsetCmd, 1}, + {"lsort", Tcl_LsortObjCmd, NULL, 1}, + {"namespace", Tcl_NamespaceObjCmd, TclCompileNamespaceCmd, 1}, + {"package", Tcl_PackageObjCmd, NULL, 1}, + {"proc", Tcl_ProcObjCmd, NULL, 1}, + {"regexp", Tcl_RegexpObjCmd, TclCompileRegexpCmd, 1}, + {"regsub", Tcl_RegsubObjCmd, NULL, 1}, + {"rename", Tcl_RenameObjCmd, NULL, 1}, + {"return", Tcl_ReturnObjCmd, TclCompileReturnCmd, 1}, + {"scan", Tcl_ScanObjCmd, NULL, 1}, + {"set", Tcl_SetObjCmd, TclCompileSetCmd, 1}, + {"split", Tcl_SplitObjCmd, NULL, 1}, + {"subst", Tcl_SubstObjCmd, NULL, 1}, + {"switch", Tcl_SwitchObjCmd, TclCompileSwitchCmd, 1}, + {"trace", Tcl_TraceObjCmd, NULL, 1}, + {"unset", Tcl_UnsetObjCmd, NULL, 1}, + {"uplevel", Tcl_UplevelObjCmd, NULL, 1}, + {"upvar", Tcl_UpvarObjCmd, TclCompileUpvarCmd, 1}, + {"variable", Tcl_VariableObjCmd, TclCompileVariableCmd, 1}, + {"while", Tcl_WhileObjCmd, TclCompileWhileCmd, 1}, + + /* + * Commands in the OS-interface. Note that many of these are unsafe. + */ + + {"after", Tcl_AfterObjCmd, NULL, 1}, + {"cd", Tcl_CdObjCmd, NULL, 0}, + {"close", Tcl_CloseObjCmd, NULL, 1}, + {"eof", Tcl_EofObjCmd, NULL, 1}, + {"encoding", Tcl_EncodingObjCmd, NULL, 0}, + {"exec", Tcl_ExecObjCmd, NULL, 0}, + {"exit", Tcl_ExitObjCmd, NULL, 0}, + {"fblocked", Tcl_FblockedObjCmd, NULL, 1}, + {"fconfigure", Tcl_FconfigureObjCmd, NULL, 0}, + {"fcopy", Tcl_FcopyObjCmd, NULL, 1}, + {"file", Tcl_FileObjCmd, NULL, 0}, + {"fileevent", Tcl_FileEventObjCmd, NULL, 1}, + {"flush", Tcl_FlushObjCmd, NULL, 1}, + {"gets", Tcl_GetsObjCmd, NULL, 1}, + {"glob", Tcl_GlobObjCmd, NULL, 0}, + {"load", Tcl_LoadObjCmd, NULL, 0}, + {"open", Tcl_OpenObjCmd, NULL, 0}, + {"pid", Tcl_PidObjCmd, NULL, 1}, + {"puts", Tcl_PutsObjCmd, NULL, 1}, + {"pwd", Tcl_PwdObjCmd, NULL, 0}, + {"read", Tcl_ReadObjCmd, NULL, 1}, + {"seek", Tcl_SeekObjCmd, NULL, 1}, + {"socket", Tcl_SocketObjCmd, NULL, 0}, + {"source", Tcl_SourceObjCmd, NULL, 0}, + {"tell", Tcl_TellObjCmd, NULL, 1}, + {"time", Tcl_TimeObjCmd, NULL, 1}, + {"unload", Tcl_UnloadObjCmd, NULL, 0}, + {"update", Tcl_UpdateObjCmd, NULL, 1}, + {"vwait", Tcl_VwaitObjCmd, NULL, 1}, + {NULL, NULL, NULL, 0} +}; + +/* + * Math functions. All are safe. + */ + +typedef struct { + const char *name; /* Name of the function. The full name is + * "::tcl::mathfunc::<name>". */ + Tcl_ObjCmdProc *objCmdProc; /* Function that evaluates the function */ + ClientData clientData; /* Client data for the function */ +} BuiltinFuncDef; +static const BuiltinFuncDef BuiltinFuncTable[] = { + { "abs", ExprAbsFunc, NULL }, + { "acos", ExprUnaryFunc, (ClientData) acos }, + { "asin", ExprUnaryFunc, (ClientData) asin }, + { "atan", ExprUnaryFunc, (ClientData) atan }, + { "atan2", ExprBinaryFunc, (ClientData) atan2 }, + { "bool", ExprBoolFunc, NULL }, + { "ceil", ExprCeilFunc, NULL }, + { "cos", ExprUnaryFunc, (ClientData) cos }, + { "cosh", ExprUnaryFunc, (ClientData) cosh }, + { "double", ExprDoubleFunc, NULL }, + { "entier", ExprEntierFunc, NULL }, + { "exp", ExprUnaryFunc, (ClientData) exp }, + { "floor", ExprFloorFunc, NULL }, + { "fmod", ExprBinaryFunc, (ClientData) fmod }, + { "hypot", ExprBinaryFunc, (ClientData) hypot }, + { "int", ExprIntFunc, NULL }, + { "isqrt", ExprIsqrtFunc, NULL }, + { "log", ExprUnaryFunc, (ClientData) log }, + { "log10", ExprUnaryFunc, (ClientData) log10 }, + { "pow", ExprBinaryFunc, (ClientData) pow }, + { "rand", ExprRandFunc, NULL }, + { "round", ExprRoundFunc, NULL }, + { "sin", ExprUnaryFunc, (ClientData) sin }, + { "sinh", ExprUnaryFunc, (ClientData) sinh }, + { "sqrt", ExprSqrtFunc, NULL }, + { "srand", ExprSrandFunc, NULL }, + { "tan", ExprUnaryFunc, (ClientData) tan }, + { "tanh", ExprUnaryFunc, (ClientData) tanh }, + { "wide", ExprWideFunc, NULL }, + { NULL, NULL, NULL } +}; + +/* + * TIP#174's math operators. All are safe. + */ + +typedef struct { + const char *name; /* Name of object-based command. */ + Tcl_ObjCmdProc *objProc; /* Object-based function for command. */ + CompileProc *compileProc; /* Function called to compile command. */ + union { + int numArgs; + int identity; + } i; + const char *expected; /* For error message, what argument(s) + * were expected. */ +} OpCmdInfo; +static const OpCmdInfo mathOpCmds[] = { + { "~", TclSingleOpCmd, TclCompileInvertOpCmd, + /* numArgs */ {1}, "integer"}, + { "!", TclSingleOpCmd, TclCompileNotOpCmd, + /* numArgs */ {1}, "boolean"}, + { "+", TclVariadicOpCmd, TclCompileAddOpCmd, + /* identity */ {0}, NULL}, + { "*", TclVariadicOpCmd, TclCompileMulOpCmd, + /* identity */ {1}, NULL}, + { "&", TclVariadicOpCmd, TclCompileAndOpCmd, + /* identity */ {-1}, NULL}, + { "|", TclVariadicOpCmd, TclCompileOrOpCmd, + /* identity */ {0}, NULL}, + { "^", TclVariadicOpCmd, TclCompileXorOpCmd, + /* identity */ {0}, NULL}, + { "**", TclVariadicOpCmd, TclCompilePowOpCmd, + /* identity */ {1}, NULL}, + { "<<", TclSingleOpCmd, TclCompileLshiftOpCmd, + /* numArgs */ {2}, "integer shift"}, + { ">>", TclSingleOpCmd, TclCompileRshiftOpCmd, + /* numArgs */ {2}, "integer shift"}, + { "%", TclSingleOpCmd, TclCompileModOpCmd, + /* numArgs */ {2}, "integer integer"}, + { "!=", TclSingleOpCmd, TclCompileNeqOpCmd, + /* numArgs */ {2}, "value value"}, + { "ne", TclSingleOpCmd, TclCompileStrneqOpCmd, + /* numArgs */ {2}, "value value"}, + { "in", TclSingleOpCmd, TclCompileInOpCmd, + /* numArgs */ {2}, "value list"}, + { "ni", TclSingleOpCmd, TclCompileNiOpCmd, + /* numArgs */ {2}, "value list"}, + { "-", TclNoIdentOpCmd, TclCompileMinusOpCmd, + /* unused */ {0}, "value ?value ...?"}, + { "/", TclNoIdentOpCmd, TclCompileDivOpCmd, + /* unused */ {0}, "value ?value ...?"}, + { "<", TclSortingOpCmd, TclCompileLessOpCmd, + /* unused */ {0}, NULL}, + { "<=", TclSortingOpCmd, TclCompileLeqOpCmd, + /* unused */ {0}, NULL}, + { ">", TclSortingOpCmd, TclCompileGreaterOpCmd, + /* unused */ {0}, NULL}, + { ">=", TclSortingOpCmd, TclCompileGeqOpCmd, + /* unused */ {0}, NULL}, + { "==", TclSortingOpCmd, TclCompileEqOpCmd, + /* unused */ {0}, NULL}, + { "eq", TclSortingOpCmd, TclCompileStreqOpCmd, + /* unused */ {0}, NULL}, + { NULL, NULL, NULL, + {0}, NULL} }; /* - * The following structure holds the client data for string-based - * trace procs + * Macros for stack checks. The goal of these macros is to allow the size of + * the stack to be checked (so preventing overflow) in a *cheap* way. Note + * that the check needs to be (amortized) cheap since it is on the critical + * path for recursion. */ -typedef struct StringTraceData { - ClientData clientData; /* Client data from Tcl_CreateTrace */ - Tcl_CmdTraceProc* proc; /* Trace procedure from Tcl_CreateTrace */ -} StringTraceData; +#if defined(TCL_NO_STACK_CHECK) +/* + * Stack check disabled: make them noops. + */ + +# define CheckCStack(interp, localIntPtr) 1 +# define GetCStackParams(iPtr) /* do nothing */ +#elif defined(TCL_CROSS_COMPILE) + +/* + * This variable is static and only set *once*, during library initialization. + * It therefore needs no thread guards. + */ + +static int stackGrowsDown = 1; +# define GetCStackParams(iPtr) \ + stackGrowsDown = TclpGetCStackParams(&((iPtr)->stackBound)) +# define CheckCStack(iPtr, localIntPtr) \ + (stackGrowsDown \ + ? ((localIntPtr) > (iPtr)->stackBound) \ + : ((localIntPtr) < (iPtr)->stackBound) \ + ) +#else /* !TCL_NO_STACK_CHECK && !TCL_CROSS_COMPILE */ +# define GetCStackParams(iPtr) \ + TclpGetCStackParams(&((iPtr)->stackBound)) +# ifdef TCL_STACK_GROWS_UP +# define CheckCStack(iPtr, localIntPtr) \ + (!(iPtr)->stackBound || (localIntPtr) < (iPtr)->stackBound) +# else /* TCL_STACK_GROWS_UP */ +# define CheckCStack(iPtr, localIntPtr) \ + ((localIntPtr) > (iPtr)->stackBound) +# endif /* TCL_STACK_GROWS_UP */ +#endif /* TCL_NO_STACK_CHECK/TCL_CROSS_COMPILE */ /* *---------------------------------------------------------------------- @@ -275,28 +367,26 @@ typedef struct StringTraceData { * Create a new TCL command interpreter. * * Results: - * The return value is a token for the interpreter, which may be - * used in calls to procedures like Tcl_CreateCmd, Tcl_Eval, or - * Tcl_DeleteInterp. + * The return value is a token for the interpreter, which may be used in + * calls to functions like Tcl_CreateCmd, Tcl_Eval, or Tcl_DeleteInterp. * * Side effects: - * The command interpreter is initialized with the built-in commands - * and with the variables documented in tclvars(n). + * The command interpreter is initialized with the built-in commands and + * with the variables documented in tclvars(n). * *---------------------------------------------------------------------- */ Tcl_Interp * -Tcl_CreateInterp() +Tcl_CreateInterp(void) { Interp *iPtr; Tcl_Interp *interp; Command *cmdPtr; - BuiltinFunc *builtinFuncPtr; - MathFunc *mathFuncPtr; - Tcl_HashEntry *hPtr; - CONST CmdInfo *cmdInfoPtr; - int i; + const BuiltinFuncDef *builtinFuncPtr; + const OpCmdInfo *opcmdInfoPtr; + const CmdInfo *cmdInfoPtr; + Tcl_Namespace *mathfuncNSPtr, *mathopNSPtr; union { char c[sizeof(short)]; short s; @@ -304,17 +394,20 @@ Tcl_CreateInterp() #ifdef TCL_COMPILE_STATS ByteCodeStats *statsPtr; #endif /* TCL_COMPILE_STATS */ + char mathFuncName[32]; + CallFrame *framePtr; + int result; - TclInitSubsystems(NULL); + TclInitSubsystems(); /* - * Panic if someone updated the CallFrame structure without - * also updating the Tcl_CallFrame structure (or vice versa). - */ + * Panic if someone updated the CallFrame structure without also updating + * the Tcl_CallFrame structure (or vice versa). + */ if (sizeof(Tcl_CallFrame) < sizeof(CallFrame)) { /*NOTREACHED*/ - panic("Tcl_CallFrame must not be smaller than CallFrame"); + Tcl_Panic("Tcl_CallFrame must not be smaller than CallFrame"); } #if defined(_WIN32) && !defined(_WIN64) @@ -330,50 +423,58 @@ Tcl_CreateInterp() /* * Initialize support for namespaces and create the global namespace - * (whose name is ""; an alias is "::"). This also initializes the - * Tcl object type table and other object management code. + * (whose name is ""; an alias is "::"). This also initializes the Tcl + * object type table and other object management code. */ iPtr = (Interp *) ckalloc(sizeof(Interp)); interp = (Tcl_Interp *) iPtr; - iPtr->result = iPtr->resultSpace; - iPtr->freeProc = NULL; - iPtr->errorLine = 0; - iPtr->objResultPtr = Tcl_NewObj(); + iPtr->result = iPtr->resultSpace; + iPtr->freeProc = NULL; + iPtr->errorLine = 0; + iPtr->objResultPtr = Tcl_NewObj(); Tcl_IncrRefCount(iPtr->objResultPtr); - iPtr->handle = TclHandleCreate(iPtr); - iPtr->globalNsPtr = NULL; - iPtr->hiddenCmdTablePtr = NULL; - iPtr->interpInfo = NULL; - Tcl_InitHashTable(&iPtr->mathFuncTable, TCL_STRING_KEYS); + iPtr->handle = TclHandleCreate(iPtr); + iPtr->globalNsPtr = NULL; + iPtr->hiddenCmdTablePtr = NULL; + iPtr->interpInfo = NULL; iPtr->numLevels = 0; iPtr->maxNestingDepth = MAX_NESTING_DEPTH; - iPtr->framePtr = NULL; - iPtr->varFramePtr = NULL; + iPtr->framePtr = NULL; /* Initialise as soon as :: is available */ + iPtr->varFramePtr = NULL; /* Initialise as soon as :: is available */ -#ifdef TCL_TIP280 /* * TIP #280 - Initialize the arrays used to extend the ByteCode and * Proc structures. */ - iPtr->cmdFramePtr = NULL; - iPtr->linePBodyPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); - iPtr->lineBCPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); - iPtr->lineLAPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); - iPtr->lineLABCPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); + + iPtr->cmdFramePtr = NULL; + iPtr->linePBodyPtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + iPtr->lineBCPtr = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + iPtr->lineLAPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); + iPtr->lineLABCPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); Tcl_InitHashTable(iPtr->linePBodyPtr, TCL_ONE_WORD_KEYS); - Tcl_InitHashTable(iPtr->lineBCPtr, TCL_ONE_WORD_KEYS); - Tcl_InitHashTable(iPtr->lineLAPtr, TCL_ONE_WORD_KEYS); - Tcl_InitHashTable(iPtr->lineLABCPtr, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(iPtr->lineBCPtr, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(iPtr->lineLAPtr, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(iPtr->lineLABCPtr, TCL_ONE_WORD_KEYS); iPtr->scriptCLLocPtr = NULL; -#endif iPtr->activeVarTracePtr = NULL; - iPtr->returnCode = TCL_OK; + + iPtr->returnOpts = NULL; iPtr->errorInfo = NULL; + TclNewLiteralStringObj(iPtr->eiVar, "::errorInfo"); + Tcl_IncrRefCount(iPtr->eiVar); iPtr->errorCode = NULL; + TclNewLiteralStringObj(iPtr->ecVar, "::errorCode"); + Tcl_IncrRefCount(iPtr->ecVar); + iPtr->returnLevel = 1; + iPtr->returnCode = TCL_OK; + + iPtr->rootFramePtr = NULL; /* Initialise as soon as :: is available */ + iPtr->lookupNsPtr = NULL; iPtr->appendResult = NULL; iPtr->appendAvl = 0; @@ -381,14 +482,15 @@ Tcl_CreateInterp() Tcl_InitHashTable(&iPtr->packageTable, TCL_STRING_KEYS); iPtr->packageUnknown = NULL; -#ifdef TCL_TIP268 + /* TIP #268 */ - iPtr->packagePrefer = (getenv ("TCL_PKG_PREFER_LATEST") == NULL ? - PKG_PREFER_STABLE : - PKG_PREFER_LATEST); -#endif + if (getenv("TCL_PKG_PREFER_LATEST") == NULL) { + iPtr->packagePrefer = PKG_PREFER_STABLE; + } else { + iPtr->packagePrefer = PKG_PREFER_LATEST; + } + iPtr->cmdCount = 0; - iPtr->termOffset = 0; TclInitLiteralTable(&(iPtr->literalTable)); iPtr->compileEpoch = 0; iPtr->compiledProcPtr = NULL; @@ -400,21 +502,58 @@ Tcl_CreateInterp() iPtr->tracesForbiddingInline = 0; iPtr->activeCmdTracePtr = NULL; iPtr->activeInterpTracePtr = NULL; - iPtr->assocData = (Tcl_HashTable *) NULL; - iPtr->execEnvPtr = NULL; /* set after namespaces initialized */ - iPtr->emptyObjPtr = Tcl_NewObj(); /* another empty object */ + iPtr->assocData = NULL; + iPtr->execEnvPtr = NULL; /* Set after namespaces initialized. */ + iPtr->emptyObjPtr = Tcl_NewObj(); + /* Another empty object. */ Tcl_IncrRefCount(iPtr->emptyObjPtr); iPtr->resultSpace[0] = 0; iPtr->threadId = Tcl_GetCurrentThread(); - iPtr->globalNsPtr = NULL; /* force creation of global ns below */ + /* TIP #378 */ +#ifdef TCL_INTERP_DEBUG_FRAME + iPtr->flags |= INTERP_DEBUG_FRAME; +#else + if (getenv("TCL_INTERP_DEBUG_FRAME") != NULL) { + iPtr->flags |= INTERP_DEBUG_FRAME; + } +#endif + + /* + * Initialise the tables for variable traces and searches *before* + * creating the global ns - so that the trace on errorInfo can be + * recorded. + */ + + Tcl_InitHashTable(&iPtr->varTraces, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&iPtr->varSearches, TCL_ONE_WORD_KEYS); + + iPtr->globalNsPtr = NULL; /* Force creation of global ns below. */ iPtr->globalNsPtr = (Namespace *) Tcl_CreateNamespace(interp, "", - (ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL); + NULL, NULL); if (iPtr->globalNsPtr == NULL) { - panic("Tcl_CreateInterp: can't create global namespace"); + Tcl_Panic("Tcl_CreateInterp: can't create global namespace"); } /* + * Initialise the rootCallframe. It cannot be allocated on the stack, as + * it has to be in place before TclCreateExecEnv tries to use a variable. + */ + + /* This is needed to satisfy GCC 3.3's strict aliasing rules */ + framePtr = (CallFrame *) ckalloc(sizeof(CallFrame)); + result = Tcl_PushCallFrame(interp, (Tcl_CallFrame *) framePtr, + (Tcl_Namespace *) iPtr->globalNsPtr, /*isProcCallFrame*/ 0); + if (result != TCL_OK) { + Tcl_Panic("Tcl_CreateInterp: failed to push the root stack frame"); + } + framePtr->objc = 0; + + iPtr->framePtr = framePtr; + iPtr->varFramePtr = framePtr; + iPtr->rootFramePtr = framePtr; + + /* * Initialize support for code compilation and execution. We call * TclCreateExecEnv after initializing namespaces since it tries to * reference a Tcl variable (it links to the Tcl "tcl_traceExec" @@ -424,6 +563,12 @@ Tcl_CreateInterp() iPtr->execEnvPtr = TclCreateExecEnv(interp); /* + * TIP #219, Tcl Channel Reflection API support. + */ + + iPtr->chanMsg = NULL; + + /* * Initialize the compilation and execution statistics kept for this * interpreter. */ @@ -433,31 +578,28 @@ Tcl_CreateInterp() statsPtr->numExecutions = 0; statsPtr->numCompilations = 0; statsPtr->numByteCodesFreed = 0; - (VOID *) memset(statsPtr->instructionCount, 0, + (void) memset(statsPtr->instructionCount, 0, sizeof(statsPtr->instructionCount)); statsPtr->totalSrcBytes = 0.0; statsPtr->totalByteCodeBytes = 0.0; statsPtr->currentSrcBytes = 0.0; statsPtr->currentByteCodeBytes = 0.0; - (VOID *) memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); - (VOID *) memset(statsPtr->byteCodeCount, 0, - sizeof(statsPtr->byteCodeCount)); - (VOID *) memset(statsPtr->lifetimeCount, 0, - sizeof(statsPtr->lifetimeCount)); - - statsPtr->currentInstBytes = 0.0; - statsPtr->currentLitBytes = 0.0; + (void) memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); + (void) memset(statsPtr->byteCodeCount, 0, sizeof(statsPtr->byteCodeCount)); + (void) memset(statsPtr->lifetimeCount, 0, sizeof(statsPtr->lifetimeCount)); + + statsPtr->currentInstBytes = 0.0; + statsPtr->currentLitBytes = 0.0; statsPtr->currentExceptBytes = 0.0; - statsPtr->currentAuxBytes = 0.0; + statsPtr->currentAuxBytes = 0.0; statsPtr->currentCmdMapBytes = 0.0; - - statsPtr->numLiteralsCreated = 0; - statsPtr->totalLitStringBytes = 0.0; + + statsPtr->numLiteralsCreated = 0; + statsPtr->totalLitStringBytes = 0.0; statsPtr->currentLitStringBytes = 0.0; - (VOID *) memset(statsPtr->literalCount, 0, - sizeof(statsPtr->literalCount)); -#endif /* TCL_COMPILE_STATS */ + (void) memset(statsPtr->literalCount, 0, sizeof(statsPtr->literalCount)); +#endif /* TCL_COMPILE_STATS */ /* * Initialise the stub table pointer. @@ -465,54 +607,74 @@ Tcl_CreateInterp() iPtr->stubTable = &tclStubs; - + /* + * Initialize the ensemble error message rewriting support. + */ + + iPtr->ensembleRewrite.sourceObjs = NULL; + iPtr->ensembleRewrite.numRemovedObjs = 0; + iPtr->ensembleRewrite.numInsertedObjs = 0; + + /* + * TIP#143: Initialise the resource limit support. + */ + + TclInitLimitSupport(interp); + + /* + * Initialise the thread-specific data ekeko. + */ + +#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) + iPtr->allocCache = TclpGetAllocCache(); +#else + iPtr->allocCache = NULL; +#endif + iPtr->pendingObjDataPtr = NULL; + iPtr->asyncReadyPtr = TclGetAsyncReadyPtr(); + + /* + * Insure that the stack checking mechanism for this interp is + * initialized. + */ + + GetCStackParams(iPtr); + /* * Create the core commands. Do it here, rather than calling - * Tcl_CreateCommand, because it's faster (there's no need to check for - * a pre-existing command by the same name). If a command has a - * Tcl_CmdProc but no Tcl_ObjCmdProc, set the Tcl_ObjCmdProc to - * TclInvokeStringCommand. This is an object-based wrapper procedure - * that extracts strings, calls the string procedure, and creates an - * object for the result. Similarly, if a command has a Tcl_ObjCmdProc - * but no Tcl_CmdProc, set the Tcl_CmdProc to TclInvokeObjectCommand. - */ - - for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; - cmdInfoPtr++) { - int new; + * Tcl_CreateCommand, because it's faster (there's no need to check for a + * pre-existing command by the same name). If a command has a Tcl_CmdProc + * but no Tcl_ObjCmdProc, set the Tcl_ObjCmdProc to + * TclInvokeStringCommand. This is an object-based wrapper function that + * extracts strings, calls the string function, and creates an object for + * the result. Similarly, if a command has a Tcl_ObjCmdProc but no + * Tcl_CmdProc, set the Tcl_CmdProc to TclInvokeObjectCommand. + */ + + for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { + int isNew; Tcl_HashEntry *hPtr; - if ((cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) - && (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) - && (cmdInfoPtr->compileProc == (CompileProc *) NULL)) { - panic("Tcl_CreateInterp: builtin command with NULL string and object command procs and a NULL compile proc\n"); + if ((cmdInfoPtr->objProc == NULL) + && (cmdInfoPtr->compileProc == NULL)) { + Tcl_Panic("builtin command with NULL object command proc and a NULL compile proc"); } - + hPtr = Tcl_CreateHashEntry(&iPtr->globalNsPtr->cmdTable, - cmdInfoPtr->name, &new); - if (new) { + cmdInfoPtr->name, &isNew); + if (isNew) { cmdPtr = (Command *) ckalloc(sizeof(Command)); cmdPtr->hPtr = hPtr; cmdPtr->nsPtr = iPtr->globalNsPtr; cmdPtr->refCount = 1; cmdPtr->cmdEpoch = 0; cmdPtr->compileProc = cmdInfoPtr->compileProc; - if (cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) { - cmdPtr->proc = TclInvokeObjectCommand; - cmdPtr->clientData = (ClientData) cmdPtr; - } else { - cmdPtr->proc = cmdInfoPtr->proc; - cmdPtr->clientData = (ClientData) NULL; - } - if (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { - cmdPtr->objProc = TclInvokeStringCommand; - cmdPtr->objClientData = (ClientData) cmdPtr; - } else { - cmdPtr->objProc = cmdInfoPtr->objProc; - cmdPtr->objClientData = (ClientData) NULL; - } + cmdPtr->proc = TclInvokeObjectCommand; + cmdPtr->clientData = cmdPtr; + cmdPtr->objProc = cmdInfoPtr->objProc; + cmdPtr->objClientData = NULL; cmdPtr->deleteProc = NULL; - cmdPtr->deleteData = (ClientData) NULL; + cmdPtr->deleteData = NULL; cmdPtr->flags = 0; cmdPtr->importRefPtr = NULL; cmdPtr->tracePtr = NULL; @@ -520,6 +682,44 @@ Tcl_CreateInterp() } } + /* + * Create the "chan", "dict", "info" and "string" ensembles. Note that all + * these commands (and their subcommands that are not present in the + * global namespace) are wholly safe. + */ + + TclInitChanCmd(interp); + TclInitDictCmd(interp); + TclInitInfoCmd(interp); + TclInitStringCmd(interp); + + /* + * Register "clock" subcommands. These *do* go through + * Tcl_CreateObjCommand, since they aren't in the global namespace and + * involve ensembles. + */ + + TclClockInit(interp); + + /* + * Register the built-in functions. This is empty now that they are + * implemented as commands in the ::tcl::mathfunc namespace. + */ + + /* + * Register the default [interp bgerror] handler. + */ + + Tcl_CreateObjCommand(interp, "::tcl::Bgerror", + TclDefaultBgErrorHandlerObjCmd, NULL, NULL); + + /* + * Create an unsupported command for debugging bytecode. + */ + + Tcl_CreateObjCommand(interp, "::tcl::unsupported::disassemble", + Tcl_DisassembleObjCmd, NULL, NULL); + #ifdef USE_DTRACE /* * Register the tcl::dtrace command. @@ -532,57 +732,61 @@ Tcl_CreateInterp() * Register the builtin math functions. */ - i = 0; - for (builtinFuncPtr = tclBuiltinFuncTable; builtinFuncPtr->name != NULL; + mathfuncNSPtr = Tcl_CreateNamespace(interp, "::tcl::mathfunc", NULL,NULL); + if (mathfuncNSPtr == NULL) { + Tcl_Panic("Can't create math function namespace"); + } + strcpy(mathFuncName, "::tcl::mathfunc::"); +#define MATH_FUNC_PREFIX_LEN 17 /* == strlen("::tcl::mathfunc::") */ + for (builtinFuncPtr = BuiltinFuncTable; builtinFuncPtr->name != NULL; builtinFuncPtr++) { - Tcl_CreateMathFunc((Tcl_Interp *) iPtr, builtinFuncPtr->name, - builtinFuncPtr->numArgs, builtinFuncPtr->argTypes, - (Tcl_MathProc *) NULL, (ClientData) 0); - hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, - builtinFuncPtr->name); - if (hPtr == NULL) { - panic("Tcl_CreateInterp: Tcl_CreateMathFunc incorrectly registered '%s'", builtinFuncPtr->name); - return NULL; + strcpy(mathFuncName+MATH_FUNC_PREFIX_LEN, builtinFuncPtr->name); + Tcl_CreateObjCommand(interp, mathFuncName, + builtinFuncPtr->objCmdProc, builtinFuncPtr->clientData, NULL); + Tcl_Export(interp, mathfuncNSPtr, builtinFuncPtr->name, 0); + } + + /* + * Register the mathematical "operator" commands. [TIP #174] + */ + + mathopNSPtr = Tcl_CreateNamespace(interp, "::tcl::mathop", NULL, NULL); +#define MATH_OP_PREFIX_LEN 15 /* == strlen("::tcl::mathop::") */ + if (mathopNSPtr == NULL) { + Tcl_Panic("can't create math operator namespace"); + } + (void) Tcl_Export(interp, mathopNSPtr, "*", 1); + strcpy(mathFuncName, "::tcl::mathop::"); + for (opcmdInfoPtr=mathOpCmds ; opcmdInfoPtr->name!=NULL ; opcmdInfoPtr++){ + TclOpCmdClientData *occdPtr = (TclOpCmdClientData *) + ckalloc(sizeof(TclOpCmdClientData)); + + occdPtr->op = opcmdInfoPtr->name; + occdPtr->i.numArgs = opcmdInfoPtr->i.numArgs; + occdPtr->expected = opcmdInfoPtr->expected; + strcpy(mathFuncName + MATH_OP_PREFIX_LEN, opcmdInfoPtr->name); + cmdPtr = (Command *) Tcl_CreateObjCommand(interp, mathFuncName, + opcmdInfoPtr->objProc, occdPtr, DeleteOpCmdClientData); + if (cmdPtr == NULL) { + Tcl_Panic("failed to create math operator %s", + opcmdInfoPtr->name); + } else if (opcmdInfoPtr->compileProc != NULL) { + cmdPtr->compileProc = opcmdInfoPtr->compileProc; } - mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); - mathFuncPtr->builtinFuncIndex = i; - i++; } - iPtr->flags |= EXPR_INITIALIZED; /* * Do Multiple/Safe Interps Tcl init stuff */ TclInterpInit(interp); + TclSetupEnv(interp); /* - * We used to create the "errorInfo" and "errorCode" global vars at this - * point because so much of the Tcl implementation assumes they already - * exist. This is not quite enough, however, since they can be unset - * at any time. - * - * There are 2 choices: - * + Check every place where a GetVar of those is used - * and the NULL result is not checked (like in tclLoad.c) - * + Make SetVar,... NULL friendly - * We choose the second option because : - * + It is easy and low cost to check for NULL pointer before - * calling strlen() - * + It can be helpfull to other people using those API - * + Passing a NULL value to those closest 'meaning' is empty string - * (specially with the new objects where 0 bytes strings are ok) - * So the following init is commented out: -- dl - * - * (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorInfo", (char *) NULL, - * "", TCL_GLOBAL_ONLY); - * (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorCode", (char *) NULL, - * "NONE", TCL_GLOBAL_ONLY); + * TIP #59: Make embedded configuration information available. */ -#ifndef TCL_GENERIC_ONLY - TclSetupEnv(interp); -#endif + TclInitEmbeddedConfigurationInformation(interp); /* * Compute the byte order of this machine. @@ -596,62 +800,66 @@ Tcl_CreateInterp() Tcl_SetVar2Ex(interp, "tcl_platform", "wordSize", Tcl_NewLongObj((long) sizeof(long)), TCL_GLOBAL_ONLY); + /* TIP #291 */ + Tcl_SetVar2Ex(interp, "tcl_platform", "pointerSize", + Tcl_NewLongObj((long) sizeof(void *)), TCL_GLOBAL_ONLY); + /* * Set up other variables such as tcl_version and tcl_library */ Tcl_SetVar(interp, "tcl_patchLevel", TCL_PATCH_LEVEL, TCL_GLOBAL_ONLY); Tcl_SetVar(interp, "tcl_version", TCL_VERSION, TCL_GLOBAL_ONLY); - Tcl_TraceVar2(interp, "tcl_precision", (char *) NULL, + Tcl_TraceVar2(interp, "tcl_precision", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, - TclPrecTraceProc, (ClientData) NULL); + TclPrecTraceProc, NULL); TclpSetVariables(interp); #ifdef TCL_THREADS /* - * The existence of the "threaded" element of the tcl_platform array indicates - * that this particular Tcl shell has been compiled with threads turned on. - * Using "info exists tcl_platform(threaded)" a Tcl script can introspect on the - * interpreter level of thread safety. + * The existence of the "threaded" element of the tcl_platform array + * indicates that this particular Tcl shell has been compiled with threads + * turned on. Using "info exists tcl_platform(threaded)" a Tcl script can + * introspect on the interpreter level of thread safety. */ - - Tcl_SetVar2(interp, "tcl_platform", "threaded", "1", - TCL_GLOBAL_ONLY); + Tcl_SetVar2(interp, "tcl_platform", "threaded", "1", TCL_GLOBAL_ONLY); #endif /* * Register Tcl's version number. - * TIP#268: Expose information about its status, - * for runtime switches in the core library - * and tests. + * TIP #268: Full patchlevel instead of just major.minor */ - Tcl_PkgProvideEx(interp, "Tcl", TCL_VERSION, (ClientData) &tclStubs); + Tcl_PkgProvideEx(interp, "Tcl", TCL_PATCH_LEVEL, &tclStubs); -#ifdef TCL_TIP268 - Tcl_SetVar2(interp, "tcl_platform", "tip,268", "1", - TCL_GLOBAL_ONLY); -#endif -#ifdef TCL_TIP280 - Tcl_SetVar2(interp, "tcl_platform", "tip,280", "1", - TCL_GLOBAL_ONLY); -#endif #ifdef Tcl_InitStubs #undef Tcl_InitStubs #endif Tcl_InitStubs(interp, TCL_VERSION, 1); + if (TclTommath_Init(interp) != TCL_OK) { + Tcl_Panic("%s", Tcl_GetString(Tcl_GetObjResult(interp))); + } + return interp; } + +static void +DeleteOpCmdClientData( + ClientData clientData) +{ + TclOpCmdClientData *occdPtr = clientData; + + ckfree((char *) occdPtr); +} /* *---------------------------------------------------------------------- * * TclHideUnsafeCommands -- * - * Hides base commands that are not marked as safe from this - * interpreter. + * Hides base commands that are not marked as safe from this interpreter. * * Results: * TCL_OK if it succeeds, TCL_ERROR else. @@ -663,18 +871,18 @@ Tcl_CreateInterp() */ int -TclHideUnsafeCommands(interp) - Tcl_Interp *interp; /* Hide commands in this interpreter. */ +TclHideUnsafeCommands( + Tcl_Interp *interp) /* Hide commands in this interpreter. */ { - register CONST CmdInfo *cmdInfoPtr; + register const CmdInfo *cmdInfoPtr; - if (interp == (Tcl_Interp *) NULL) { - return TCL_ERROR; + if (interp == NULL) { + return TCL_ERROR; } for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { - if (!cmdInfoPtr->isSafe) { - Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); - } + if (!cmdInfoPtr->isSafe) { + Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); + } } return TCL_OK; } @@ -684,36 +892,34 @@ TclHideUnsafeCommands(interp) * * Tcl_CallWhenDeleted -- * - * Arrange for a procedure to be called before a given - * interpreter is deleted. The procedure is called as soon - * as Tcl_DeleteInterp is called; if Tcl_CallWhenDeleted is - * called on an interpreter that has already been deleted, - * the procedure will be called when the last Tcl_Release is + * Arrange for a function to be called before a given interpreter is + * deleted. The function is called as soon as Tcl_DeleteInterp is called; + * if Tcl_CallWhenDeleted is called on an interpreter that has already + * been deleted, the function will be called when the last Tcl_Release is * done on the interpreter. * * Results: * None. * * Side effects: - * When Tcl_DeleteInterp is invoked to delete interp, - * proc will be invoked. See the manual entry for - * details. + * When Tcl_DeleteInterp is invoked to delete interp, proc will be + * invoked. See the manual entry for details. * *-------------------------------------------------------------- */ void -Tcl_CallWhenDeleted(interp, proc, clientData) - Tcl_Interp *interp; /* Interpreter to watch. */ - Tcl_InterpDeleteProc *proc; /* Procedure to call when interpreter - * is about to be deleted. */ - ClientData clientData; /* One-word value to pass to proc. */ +Tcl_CallWhenDeleted( + Tcl_Interp *interp, /* Interpreter to watch. */ + Tcl_InterpDeleteProc *proc, /* Function to call when interpreter is about + * to be deleted. */ + ClientData clientData) /* One-word value to pass to proc. */ { Interp *iPtr = (Interp *) interp; static Tcl_ThreadDataKey assocDataCounterKey; int *assocDataCounterPtr = Tcl_GetThreadData(&assocDataCounterKey, (int)sizeof(int)); - int new; + int isNew; char buffer[32 + TCL_INTEGER_SPACE]; AssocData *dPtr = (AssocData *) ckalloc(sizeof(AssocData)); Tcl_HashEntry *hPtr; @@ -721,11 +927,11 @@ Tcl_CallWhenDeleted(interp, proc, clientData) sprintf(buffer, "Assoc Data Key #%d", *assocDataCounterPtr); (*assocDataCounterPtr)++; - if (iPtr->assocData == (Tcl_HashTable *) NULL) { - iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); + if (iPtr->assocData == NULL) { + iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); } - hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &new); + hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &isNew); dPtr->proc = proc; dPtr->clientData = clientData; Tcl_SetHashValue(hPtr, dPtr); @@ -736,27 +942,26 @@ Tcl_CallWhenDeleted(interp, proc, clientData) * * Tcl_DontCallWhenDeleted -- * - * Cancel the arrangement for a procedure to be called when - * a given interpreter is deleted. + * Cancel the arrangement for a function to be called when a given + * interpreter is deleted. * * Results: * None. * * Side effects: - * If proc and clientData were previously registered as a - * callback via Tcl_CallWhenDeleted, they are unregistered. - * If they weren't previously registered then nothing - * happens. + * If proc and clientData were previously registered as a callback via + * Tcl_CallWhenDeleted, they are unregistered. If they weren't previously + * registered then nothing happens. * *-------------------------------------------------------------- */ void -Tcl_DontCallWhenDeleted(interp, proc, clientData) - Tcl_Interp *interp; /* Interpreter to watch. */ - Tcl_InterpDeleteProc *proc; /* Procedure to call when interpreter - * is about to be deleted. */ - ClientData clientData; /* One-word value to pass to proc. */ +Tcl_DontCallWhenDeleted( + Tcl_Interp *interp, /* Interpreter to watch. */ + Tcl_InterpDeleteProc *proc, /* Function to call when interpreter is about + * to be deleted. */ + ClientData clientData) /* One-word value to pass to proc. */ { Interp *iPtr = (Interp *) interp; Tcl_HashTable *hTablePtr; @@ -765,17 +970,17 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData) AssocData *dPtr; hTablePtr = iPtr->assocData; - if (hTablePtr == (Tcl_HashTable *) NULL) { - return; + if (hTablePtr == NULL) { + return; } for (hPtr = Tcl_FirstHashEntry(hTablePtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - dPtr = (AssocData *) Tcl_GetHashValue(hPtr); - if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { - ckfree((char *) dPtr); - Tcl_DeleteHashEntry(hPtr); - return; - } + dPtr = (AssocData *) Tcl_GetHashValue(hPtr); + if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { + ckfree((char *) dPtr); + Tcl_DeleteHashEntry(hPtr); + return; + } } } @@ -785,9 +990,9 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData) * Tcl_SetAssocData -- * * Creates a named association between user-specified data, a delete - * function and this interpreter. If the association already exists - * the data is overwritten with the new data. The delete function will - * be invoked when the interpreter is deleted. + * function and this interpreter. If the association already exists the + * data is overwritten with the new data. The delete function will be + * invoked when the interpreter is deleted. * * Results: * None. @@ -799,27 +1004,27 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData) */ void -Tcl_SetAssocData(interp, name, proc, clientData) - Tcl_Interp *interp; /* Interpreter to associate with. */ - CONST char *name; /* Name for association. */ - Tcl_InterpDeleteProc *proc; /* Proc to call when interpreter is - * about to be deleted. */ - ClientData clientData; /* One-word value to pass to proc. */ +Tcl_SetAssocData( + Tcl_Interp *interp, /* Interpreter to associate with. */ + const char *name, /* Name for association. */ + Tcl_InterpDeleteProc *proc, /* Proc to call when interpreter is about to + * be deleted. */ + ClientData clientData) /* One-word value to pass to proc. */ { Interp *iPtr = (Interp *) interp; AssocData *dPtr; Tcl_HashEntry *hPtr; - int new; + int isNew; - if (iPtr->assocData == (Tcl_HashTable *) NULL) { - iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); + if (iPtr->assocData == NULL) { + iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); + Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); } - hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &new); - if (new == 0) { - dPtr = (AssocData *) Tcl_GetHashValue(hPtr); + hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &isNew); + if (isNew == 0) { + dPtr = Tcl_GetHashValue(hPtr); } else { - dPtr = (AssocData *) ckalloc(sizeof(AssocData)); + dPtr = (AssocData *) ckalloc(sizeof(AssocData)); } dPtr->proc = proc; dPtr->clientData = clientData; @@ -832,8 +1037,8 @@ Tcl_SetAssocData(interp, name, proc, clientData) * * Tcl_DeleteAssocData -- * - * Deletes a named association of user-specified data with - * the specified interpreter. + * Deletes a named association of user-specified data with the specified + * interpreter. * * Results: * None. @@ -845,24 +1050,24 @@ Tcl_SetAssocData(interp, name, proc, clientData) */ void -Tcl_DeleteAssocData(interp, name) - Tcl_Interp *interp; /* Interpreter to associate with. */ - CONST char *name; /* Name of association. */ +Tcl_DeleteAssocData( + Tcl_Interp *interp, /* Interpreter to associate with. */ + const char *name) /* Name of association. */ { Interp *iPtr = (Interp *) interp; AssocData *dPtr; Tcl_HashEntry *hPtr; - if (iPtr->assocData == (Tcl_HashTable *) NULL) { - return; + if (iPtr->assocData == NULL) { + return; } hPtr = Tcl_FindHashEntry(iPtr->assocData, name); - if (hPtr == (Tcl_HashEntry *) NULL) { - return; + if (hPtr == NULL) { + return; } - dPtr = (AssocData *) Tcl_GetHashValue(hPtr); + dPtr = Tcl_GetHashValue(hPtr); if (dPtr->proc != NULL) { - (dPtr->proc) (dPtr->clientData, interp); + dPtr->proc(dPtr->clientData, interp); } ckfree((char *) dPtr); Tcl_DeleteHashEntry(hPtr); @@ -873,8 +1078,8 @@ Tcl_DeleteAssocData(interp, name) * * Tcl_GetAssocData -- * - * Returns the client data associated with this name in the - * specified interpreter. + * Returns the client data associated with this name in the specified + * interpreter. * * Results: * The client data in the AssocData record denoted by the named @@ -887,26 +1092,27 @@ Tcl_DeleteAssocData(interp, name) */ ClientData -Tcl_GetAssocData(interp, name, procPtr) - Tcl_Interp *interp; /* Interpreter associated with. */ - CONST char *name; /* Name of association. */ - Tcl_InterpDeleteProc **procPtr; /* Pointer to place to store address - * of current deletion callback. */ +Tcl_GetAssocData( + Tcl_Interp *interp, /* Interpreter associated with. */ + const char *name, /* Name of association. */ + Tcl_InterpDeleteProc **procPtr) + /* Pointer to place to store address of + * current deletion callback. */ { Interp *iPtr = (Interp *) interp; AssocData *dPtr; Tcl_HashEntry *hPtr; - if (iPtr->assocData == (Tcl_HashTable *) NULL) { - return (ClientData) NULL; + if (iPtr->assocData == NULL) { + return NULL; } hPtr = Tcl_FindHashEntry(iPtr->assocData, name); - if (hPtr == (Tcl_HashEntry *) NULL) { - return (ClientData) NULL; + if (hPtr == NULL) { + return NULL; } - dPtr = (AssocData *) Tcl_GetHashValue(hPtr); - if (procPtr != (Tcl_InterpDeleteProc **) NULL) { - *procPtr = dPtr->proc; + dPtr = Tcl_GetHashValue(hPtr); + if (procPtr != NULL) { + *procPtr = dPtr->proc; } return dPtr->clientData; } @@ -916,8 +1122,8 @@ Tcl_GetAssocData(interp, name, procPtr) * * Tcl_InterpDeleted -- * - * Returns nonzero if the interpreter has been deleted with a call - * to Tcl_DeleteInterp. + * Returns nonzero if the interpreter has been deleted with a call to + * Tcl_DeleteInterp. * * Results: * Nonzero if the interpreter is deleted, zero otherwise. @@ -929,8 +1135,8 @@ Tcl_GetAssocData(interp, name, procPtr) */ int -Tcl_InterpDeleted(interp) - Tcl_Interp *interp; +Tcl_InterpDeleted( + Tcl_Interp *interp) { return (((Interp *) interp)->flags & DELETED) ? 1 : 0; } @@ -940,11 +1146,11 @@ Tcl_InterpDeleted(interp) * * Tcl_DeleteInterp -- * - * Ensures that the interpreter will be deleted eventually. If there - * are no Tcl_Preserve calls in effect for this interpreter, it is - * deleted immediately, otherwise the interpreter is deleted when - * the last Tcl_Preserve is matched by a call to Tcl_Release. In either - * case, the procedure runs the currently registered deletion callbacks. + * Ensures that the interpreter will be deleted eventually. If there are + * no Tcl_Preserve calls in effect for this interpreter, it is deleted + * immediately, otherwise the interpreter is deleted when the last + * Tcl_Preserve is matched by a call to Tcl_Release. In either case, the + * function runs the currently registered deletion callbacks. * * Results: * None. @@ -959,9 +1165,9 @@ Tcl_InterpDeleted(interp) */ void -Tcl_DeleteInterp(interp) - Tcl_Interp *interp; /* Token for command interpreter (returned - * by a previous call to Tcl_CreateInterp). */ +Tcl_DeleteInterp( + Tcl_Interp *interp) /* Token for command interpreter (returned by + * a previous call to Tcl_CreateInterp). */ { Interp *iPtr = (Interp *) interp; @@ -970,21 +1176,22 @@ Tcl_DeleteInterp(interp) */ if (iPtr->flags & DELETED) { - return; + return; } - + /* * Mark the interpreter as deleted. No further evals will be allowed. + * Increase the compileEpoch as a signal to compiled bytecodes. */ iPtr->flags |= DELETED; + iPtr->compileEpoch++; /* * Ensure that the interpreter is eventually deleted. */ - Tcl_EventuallyFree((ClientData) interp, - (Tcl_FreeProc *) DeleteInterpProc); + Tcl_EventuallyFree(interp, (Tcl_FreeProc *) DeleteInterpProc); } /* @@ -992,25 +1199,25 @@ Tcl_DeleteInterp(interp) * * DeleteInterpProc -- * - * Helper procedure to delete an interpreter. This procedure is - * called when the last call to Tcl_Preserve on this interpreter - * is matched by a call to Tcl_Release. The procedure cleans up - * all resources used in the interpreter and calls all currently - * registered interpreter deletion callbacks. + * Helper function to delete an interpreter. This function is called when + * the last call to Tcl_Preserve on this interpreter is matched by a call + * to Tcl_Release. The function cleans up all resources used in the + * interpreter and calls all currently registered interpreter deletion + * callbacks. * * Results: * None. * * Side effects: - * Whatever the interpreter deletion callbacks do. Frees resources - * used by the interpreter. + * Whatever the interpreter deletion callbacks do. Frees resources used + * by the interpreter. * *---------------------------------------------------------------------- */ static void -DeleteInterpProc(interp) - Tcl_Interp *interp; /* Interpreter to delete. */ +DeleteInterpProc( + Tcl_Interp *interp) /* Interpreter to delete. */ { Interp *iPtr = (Interp *) interp; Tcl_HashEntry *hPtr; @@ -1021,120 +1228,137 @@ DeleteInterpProc(interp) /* * Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup. */ - + if (iPtr->numLevels > 0) { - panic("DeleteInterpProc called with active evals"); + Tcl_Panic("DeleteInterpProc called with active evals"); } /* - * The interpreter should already be marked deleted; otherwise how - * did we get here? + * The interpreter should already be marked deleted; otherwise how did we + * get here? */ if (!(iPtr->flags & DELETED)) { - panic("DeleteInterpProc called on interpreter not marked deleted"); + Tcl_Panic("DeleteInterpProc called on interpreter not marked deleted"); } - TclHandleFree(iPtr->handle); + /* + * TIP #219, Tcl Channel Reflection API. Discard a leftover state. + */ + + if (iPtr->chanMsg != NULL) { + Tcl_DecrRefCount(iPtr->chanMsg); + iPtr->chanMsg = NULL; + } + + /* + * Shut down all limit handler callback scripts that call back into this + * interpreter. Then eliminate all limit handlers for this interpreter. + */ + + TclRemoveScriptLimitCallbacks(interp); + TclLimitRemoveAllHandlers(interp); /* - * Dismantle everything in the global namespace except for the - * "errorInfo" and "errorCode" variables. These remain until the - * namespace is actually destroyed, in case any errors occur. - * * Dismantle the namespace here, before we clear the assocData. If any * background errors occur here, they will be deleted below. + * + * Dismantle the namespace after freeing the iPtr->handle so that each + * bytecode releases its literals without caring to update the literal + * table, as it will be freed later in this function without further use. */ - + + TclHandleFree(iPtr->handle); TclTeardownNamespace(iPtr->globalNsPtr); /* * Delete all the hidden commands. */ - + hTablePtr = iPtr->hiddenCmdTablePtr; if (hTablePtr != NULL) { /* - * Non-pernicious deletion. The deletion callbacks will not be - * allowed to create any new hidden or non-hidden commands. + * Non-pernicious deletion. The deletion callbacks will not be allowed + * to create any new hidden or non-hidden commands. * Tcl_DeleteCommandFromToken() will remove the entry from the * hiddenCmdTablePtr. */ - + hPtr = Tcl_FirstHashEntry(hTablePtr, &search); - for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { + for (; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { Tcl_DeleteCommandFromToken(interp, (Tcl_Command) Tcl_GetHashValue(hPtr)); } Tcl_DeleteHashTable(hTablePtr); ckfree((char *) hTablePtr); } + /* - * Tear down the math function table. + * Invoke deletion callbacks; note that a callback can create new + * callbacks, so we iterate. */ - for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &search); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&search)) { - ckfree((char *) Tcl_GetHashValue(hPtr)); + while (iPtr->assocData != NULL) { + AssocData *dPtr; + + hTablePtr = iPtr->assocData; + iPtr->assocData = NULL; + for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); + hPtr != NULL; + hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { + dPtr = Tcl_GetHashValue(hPtr); + Tcl_DeleteHashEntry(hPtr); + if (dPtr->proc != NULL) { + dPtr->proc(dPtr->clientData, interp); + } + ckfree((char *) dPtr); + } + Tcl_DeleteHashTable(hTablePtr); + ckfree((char *) hTablePtr); } - Tcl_DeleteHashTable(&iPtr->mathFuncTable); /* - * Invoke deletion callbacks; note that a callback can create new - * callbacks, so we iterate. + * Pop the root frame pointer and finish deleting the global + * namespace. The order is important [Bug 1658572]. */ - while (iPtr->assocData != (Tcl_HashTable *) NULL) { - AssocData *dPtr; - - hTablePtr = iPtr->assocData; - iPtr->assocData = (Tcl_HashTable *) NULL; - for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); - hPtr != NULL; - hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { - dPtr = (AssocData *) Tcl_GetHashValue(hPtr); - Tcl_DeleteHashEntry(hPtr); - if (dPtr->proc != NULL) { - (*dPtr->proc)(dPtr->clientData, interp); - } - ckfree((char *) dPtr); - } - Tcl_DeleteHashTable(hTablePtr); - ckfree((char *) hTablePtr); - } - - /* - * Finish deleting the global namespace. - */ - + if (iPtr->framePtr != iPtr->rootFramePtr) { + Tcl_Panic("DeleteInterpProc: popping rootCallFrame with other frames on top"); + } + Tcl_PopCallFrame(interp); + ckfree((char *) iPtr->rootFramePtr); + iPtr->rootFramePtr = NULL; Tcl_DeleteNamespace((Tcl_Namespace *) iPtr->globalNsPtr); /* - * Free up the result *after* deleting variables, since variable - * deletion could have transferred ownership of the result string - * to Tcl. + * Free up the result *after* deleting variables, since variable deletion + * could have transferred ownership of the result string to Tcl. */ Tcl_FreeResult(interp); interp->result = NULL; Tcl_DecrRefCount(iPtr->objResultPtr); iPtr->objResultPtr = NULL; - if (iPtr->errorInfo != NULL) { - ckfree(iPtr->errorInfo); - iPtr->errorInfo = NULL; + Tcl_DecrRefCount(iPtr->ecVar); + if (iPtr->errorCode) { + Tcl_DecrRefCount(iPtr->errorCode); + iPtr->errorCode = NULL; } - if (iPtr->errorCode != NULL) { - ckfree(iPtr->errorCode); - iPtr->errorCode = NULL; + Tcl_DecrRefCount(iPtr->eiVar); + if (iPtr->errorInfo) { + Tcl_DecrRefCount(iPtr->errorInfo); + iPtr->errorInfo = NULL; + } + if (iPtr->returnOpts) { + Tcl_DecrRefCount(iPtr->returnOpts); } if (iPtr->appendResult != NULL) { ckfree(iPtr->appendResult); - iPtr->appendResult = NULL; + iPtr->appendResult = NULL; } TclFreePackageInfo(iPtr); while (iPtr->tracePtr != NULL) { - Tcl_DeleteTrace((Tcl_Interp*) iPtr, (Tcl_Trace) iPtr->tracePtr); + Tcl_DeleteTrace((Tcl_Interp *) iPtr, (Tcl_Trace) iPtr->tracePtr); } if (iPtr->execEnvPtr != NULL) { TclDeleteExecEnv(iPtr->execEnvPtr); @@ -1151,9 +1375,9 @@ DeleteInterpProc(interp) nextResPtr = resPtr->nextPtr; ckfree(resPtr->name); ckfree((char *) resPtr); - resPtr = nextResPtr; + resPtr = nextResPtr; } - + /* * Free up literal objects created for scripts compiled by the * interpreter. @@ -1161,60 +1385,62 @@ DeleteInterpProc(interp) TclDeleteLiteralTable(interp, &(iPtr->literalTable)); -#ifdef TCL_TIP280 - /* TIP #280 - Release the arrays for ByteCode/Proc extension, and contents. + /* + * TIP #280 - Release the arrays for ByteCode/Proc extension, and + * contents. */ + { - Tcl_HashEntry *hPtr; + Tcl_HashEntry *hPtr; Tcl_HashSearch hSearch; - CmdFrame* cfPtr; - ExtCmdLoc* eclPtr; - int i; + int i; for (hPtr = Tcl_FirstHashEntry(iPtr->linePBodyPtr, &hSearch); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&hSearch)) { - - cfPtr = (CmdFrame*) Tcl_GetHashValue (hPtr); - - if (cfPtr->type == TCL_LOCATION_SOURCE) { - Tcl_DecrRefCount (cfPtr->data.eval.path); + hPtr != NULL; + hPtr = Tcl_NextHashEntry(&hSearch)) { + CmdFrame *cfPtr = Tcl_GetHashValue(hPtr); + Proc *procPtr = (Proc *) Tcl_GetHashKey(iPtr->linePBodyPtr, hPtr); + procPtr->iPtr = NULL; + if (cfPtr) { + if (cfPtr->type == TCL_LOCATION_SOURCE) { + Tcl_DecrRefCount(cfPtr->data.eval.path); + } + ckfree((char *) cfPtr->line); + ckfree((char *) cfPtr); } - ckfree ((char*) cfPtr->line); - ckfree ((char*) cfPtr); - Tcl_DeleteHashEntry (hPtr); - + Tcl_DeleteHashEntry(hPtr); } - Tcl_DeleteHashTable (iPtr->linePBodyPtr); - ckfree ((char*) iPtr->linePBodyPtr); + Tcl_DeleteHashTable(iPtr->linePBodyPtr); + ckfree((char *) iPtr->linePBodyPtr); iPtr->linePBodyPtr = NULL; - /* See also tclCompile.c, TclCleanupByteCode */ + /* + * See also tclCompile.c, TclCleanupByteCode + */ for (hPtr = Tcl_FirstHashEntry(iPtr->lineBCPtr, &hSearch); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&hSearch)) { - - eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hPtr); + hPtr != NULL; + hPtr = Tcl_NextHashEntry(&hSearch)) { + ExtCmdLoc *eclPtr = (ExtCmdLoc *) Tcl_GetHashValue(hPtr); if (eclPtr->type == TCL_LOCATION_SOURCE) { - Tcl_DecrRefCount (eclPtr->path); + Tcl_DecrRefCount(eclPtr->path); } for (i=0; i< eclPtr->nuloc; i++) { - ckfree ((char*) eclPtr->loc[i].line); + ckfree((char *) eclPtr->loc[i].line); } - if (eclPtr->loc != NULL) { - ckfree ((char*) eclPtr->loc); + if (eclPtr->loc != NULL) { + ckfree((char *) eclPtr->loc); } Tcl_DeleteHashTable (&eclPtr->litInfo); - ckfree ((char*) eclPtr); - Tcl_DeleteHashEntry (hPtr); + ckfree((char *) eclPtr); + Tcl_DeleteHashEntry(hPtr); } - Tcl_DeleteHashTable (iPtr->lineBCPtr); - ckfree((char*) iPtr->lineBCPtr); + Tcl_DeleteHashTable(iPtr->lineBCPtr); + ckfree((char *) iPtr->lineBCPtr); iPtr->lineBCPtr = NULL; /* @@ -1250,7 +1476,10 @@ DeleteInterpProc(interp) ckfree((char*) iPtr->lineLABCPtr); iPtr->lineLABCPtr = NULL; } -#endif + + Tcl_DeleteHashTable(&iPtr->varTraces); + Tcl_DeleteHashTable(&iPtr->varSearches); + ckfree((char *) iPtr); } @@ -1259,79 +1488,77 @@ DeleteInterpProc(interp) * * Tcl_HideCommand -- * - * Makes a command hidden so that it cannot be invoked from within - * an interpreter, only from within an ancestor. + * Makes a command hidden so that it cannot be invoked from within an + * interpreter, only from within an ancestor. * * Results: - * A standard Tcl result; also leaves a message in the interp's result - * if an error occurs. + * A standard Tcl result; also leaves a message in the interp's result if + * an error occurs. * * Side effects: - * Removes a command from the command table and create an entry - * into the hidden command table under the specified token name. + * Removes a command from the command table and create an entry into the + * hidden command table under the specified token name. * *--------------------------------------------------------------------------- */ int -Tcl_HideCommand(interp, cmdName, hiddenCmdToken) - Tcl_Interp *interp; /* Interpreter in which to hide command. */ - CONST char *cmdName; /* Name of command to hide. */ - CONST char *hiddenCmdToken; /* Token name of the to-be-hidden command. */ +Tcl_HideCommand( + Tcl_Interp *interp, /* Interpreter in which to hide command. */ + const char *cmdName, /* Name of command to hide. */ + const char *hiddenCmdToken) /* Token name of the to-be-hidden command. */ { Interp *iPtr = (Interp *) interp; Tcl_Command cmd; Command *cmdPtr; Tcl_HashTable *hiddenCmdTablePtr; Tcl_HashEntry *hPtr; - int new; + int isNew; if (iPtr->flags & DELETED) { + /* + * The interpreter is being deleted. Do not create any new structures, + * because it is not safe to modify the interpreter. + */ - /* - * The interpreter is being deleted. Do not create any new - * structures, because it is not safe to modify the interpreter. - */ - - return TCL_ERROR; + return TCL_ERROR; } /* * Disallow hiding of commands that are currently in a namespace or - * renaming (as part of hiding) into a namespace. + * renaming (as part of hiding) into a namespace (because the current + * implementation with a single global table and the needed uniqueness of + * names cause problems with namespaces). * - * (because the current implementation with a single global table - * and the needed uniqueness of names cause problems with namespaces) + * We don't need to check for "::" in cmdName because the real check is on + * the nsPtr below. * - * we don't need to check for "::" in cmdName because the real check is - * on the nsPtr below. - * - * hiddenCmdToken is just a string which is not interpreted in any way. - * It may contain :: but the string is not interpreted as a namespace + * hiddenCmdToken is just a string which is not interpreted in any way. It + * may contain :: but the string is not interpreted as a namespace * qualifier command name. Thus, hiding foo::bar to foo::bar and then * trying to expose or invoke ::foo::bar will NOT work; but if the * application always uses the same strings it will get consistent * behaviour. * - * But as we currently limit ourselves to the global namespace only - * for the source, in order to avoid potential confusion, - * lets prevent "::" in the token too. --dl + * But as we currently limit ourselves to the global namespace only for + * the source, in order to avoid potential confusion, lets prevent "::" in + * the token too. - dl */ if (strstr(hiddenCmdToken, "::") != NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "cannot use namespace qualifiers in hidden command", - " token (rename)", (char *) NULL); - return TCL_ERROR; + Tcl_AppendResult(interp, + "cannot use namespace qualifiers in hidden command" + " token (rename)", NULL); + return TCL_ERROR; } /* - * Find the command to hide. An error is returned if cmdName can't - * be found. Look up the command only from the global namespace. - * Full path of the command must be given if using namespaces. + * Find the command to hide. An error is returned if cmdName can't be + * found. Look up the command only from the global namespace. Full path of + * the command must be given if using namespaces. */ - cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, + cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY); if (cmd == (Tcl_Command) NULL) { return TCL_ERROR; @@ -1342,22 +1569,21 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken) * Check that the command is really in global namespace */ - if ( cmdPtr->nsPtr != iPtr->globalNsPtr ) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "can only hide global namespace commands", - " (use rename then hide)", (char *) NULL); - return TCL_ERROR; + if (cmdPtr->nsPtr != iPtr->globalNsPtr) { + Tcl_AppendResult(interp, "can only hide global namespace commands" + " (use rename then hide)", NULL); + return TCL_ERROR; } - + /* * Initialize the hidden command table if necessary. */ hiddenCmdTablePtr = iPtr->hiddenCmdTablePtr; if (hiddenCmdTablePtr == NULL) { - hiddenCmdTablePtr = (Tcl_HashTable *) - ckalloc((unsigned) sizeof(Tcl_HashTable)); - Tcl_InitHashTable(hiddenCmdTablePtr, TCL_STRING_KEYS); + hiddenCmdTablePtr = (Tcl_HashTable *) + ckalloc((unsigned) sizeof(Tcl_HashTable)); + Tcl_InitHashTable(hiddenCmdTablePtr, TCL_STRING_KEYS); iPtr->hiddenCmdTablePtr = hiddenCmdTablePtr; } @@ -1366,20 +1592,18 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken) * hiddenCmdToken if a hidden command with the name hiddenCmdToken already * exists. */ - - hPtr = Tcl_CreateHashEntry(hiddenCmdTablePtr, hiddenCmdToken, &new); - if (!new) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "hidden command named \"", hiddenCmdToken, "\" already exists", - (char *) NULL); - return TCL_ERROR; + + hPtr = Tcl_CreateHashEntry(hiddenCmdTablePtr, hiddenCmdToken, &isNew); + if (!isNew) { + Tcl_AppendResult(interp, "hidden command named \"", hiddenCmdToken, + "\" already exists", NULL); + return TCL_ERROR; } /* - * Nb : This code is currently 'like' a rename to a specialy set apart - * name table. Changes here and in TclRenameCommand must - * be kept in synch untill the common parts are actually - * factorized out. + * NB: This code is currently 'like' a rename to a specialy set apart name + * table. Changes here and in TclRenameCommand must be kept in synch until + * the common parts are actually factorized out. */ /* @@ -1389,26 +1613,34 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken) */ if (cmdPtr->hPtr != NULL) { - Tcl_DeleteHashEntry(cmdPtr->hPtr); - cmdPtr->hPtr = (Tcl_HashEntry *) NULL; + Tcl_DeleteHashEntry(cmdPtr->hPtr); + cmdPtr->hPtr = NULL; cmdPtr->cmdEpoch++; } /* - * Now link the hash table entry with the command structure. - * We ensured above that the nsPtr was right. + * The list of command exported from the namespace might have changed. + * However, we do not need to recompute this just yet; next time we need + * the info will be soon enough. + */ + + TclInvalidateNsCmdLookup(cmdPtr->nsPtr); + + /* + * Now link the hash table entry with the command structure. We ensured + * above that the nsPtr was right. */ - + cmdPtr->hPtr = hPtr; - Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); + Tcl_SetHashValue(hPtr, cmdPtr); /* - * If the command being hidden has a compile procedure, increment the - * interpreter's compileEpoch to invalidate its compiled code. This - * makes sure that we don't later try to execute old code compiled with - * command-specific (i.e., inline) bytecodes for the now-hidden - * command. This field is checked in Tcl_EvalObj and ObjInterpProc, - * and code whose compilation epoch doesn't match is recompiled. + * If the command being hidden has a compile function, increment the + * interpreter's compileEpoch to invalidate its compiled code. This makes + * sure that we don't later try to execute old code compiled with + * command-specific (i.e., inline) bytecodes for the now-hidden command. + * This field is checked in Tcl_EvalObj and ObjInterpProc, and code whose + * compilation epoch doesn't match is recompiled. */ if (cmdPtr->compileProc != NULL) { @@ -1422,12 +1654,12 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken) * * Tcl_ExposeCommand -- * - * Makes a previously hidden command callable from inside the - * interpreter instead of only by its ancestors. + * Makes a previously hidden command callable from inside the interpreter + * instead of only by its ancestors. * * Results: - * A standard Tcl result. If an error occurs, a message is left - * in the interp's result. + * A standard Tcl result. If an error occurs, a message is left in the + * interp's result. * * Side effects: * Moves commands from one hash table to another. @@ -1436,40 +1668,38 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken) */ int -Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName) - Tcl_Interp *interp; /* Interpreter in which to make command - * callable. */ - CONST char *hiddenCmdToken; /* Name of hidden command. */ - CONST char *cmdName; /* Name of to-be-exposed command. */ +Tcl_ExposeCommand( + Tcl_Interp *interp, /* Interpreter in which to make command + * callable. */ + const char *hiddenCmdToken, /* Name of hidden command. */ + const char *cmdName) /* Name of to-be-exposed command. */ { Interp *iPtr = (Interp *) interp; Command *cmdPtr; Namespace *nsPtr; Tcl_HashEntry *hPtr; Tcl_HashTable *hiddenCmdTablePtr; - int new; + int isNew; if (iPtr->flags & DELETED) { - /* - * The interpreter is being deleted. Do not create any new - * structures, because it is not safe to modify the interpreter. - */ - - return TCL_ERROR; + /* + * The interpreter is being deleted. Do not create any new structures, + * because it is not safe to modify the interpreter. + */ + + return TCL_ERROR; } /* - * Check that we have a regular name for the command - * (that the user is not trying to do an expose and a rename - * (to another namespace) at the same time) + * Check that we have a regular name for the command (that the user is not + * trying to do an expose and a rename (to another namespace) at the same + * time). */ if (strstr(cmdName, "::") != NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "can not expose to a namespace ", - "(use expose to toplevel, then rename)", - (char *) NULL); - return TCL_ERROR; + Tcl_AppendResult(interp, "cannot expose to a namespace " + "(use expose to toplevel, then rename)", NULL); + return TCL_ERROR; } /* @@ -1481,82 +1711,90 @@ Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName) if (hiddenCmdTablePtr != NULL) { hPtr = Tcl_FindHashEntry(hiddenCmdTablePtr, hiddenCmdToken); } - if (hPtr == (Tcl_HashEntry *) NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "unknown hidden command \"", hiddenCmdToken, - "\"", (char *) NULL); - return TCL_ERROR; + if (hPtr == NULL) { + Tcl_AppendResult(interp, "unknown hidden command \"", hiddenCmdToken, + "\"", NULL); + return TCL_ERROR; } - cmdPtr = (Command *) Tcl_GetHashValue(hPtr); - + cmdPtr = Tcl_GetHashValue(hPtr); /* - * Check that we have a true global namespace - * command (enforced by Tcl_HideCommand() but let's double - * check. (If it was not, we would not really know how to - * handle it). + * Check that we have a true global namespace command (enforced by + * Tcl_HideCommand() but let's double check. (If it was not, we would not + * really know how to handle it). */ - if ( cmdPtr->nsPtr != iPtr->globalNsPtr ) { - /* - * This case is theoritically impossible, - * we might rather panic() than 'nicely' erroring out ? + + if (cmdPtr->nsPtr != iPtr->globalNsPtr) { + /* + * This case is theoritically impossible, we might rather Tcl_Panic() + * than 'nicely' erroring out ? */ - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "trying to expose a non global command name space command", - (char *) NULL); - return TCL_ERROR; + + Tcl_AppendResult(interp, + "trying to expose a non global command name space command", + NULL); + return TCL_ERROR; } - - /* This is the global table */ + + /* + * This is the global table. + */ + nsPtr = cmdPtr->nsPtr; /* - * It is an error to overwrite an existing exposed command as a result - * of exposing a previously hidden command. + * It is an error to overwrite an existing exposed command as a result of + * exposing a previously hidden command. */ - hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &new); - if (!new) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "exposed command \"", cmdName, - "\" already exists", (char *) NULL); - return TCL_ERROR; + hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &isNew); + if (!isNew) { + Tcl_AppendResult(interp, "exposed command \"", cmdName, + "\" already exists", NULL); + return TCL_ERROR; } /* + * The list of command exported from the namespace might have changed. + * However, we do not need to recompute this just yet; next time we need + * the info will be soon enough. + */ + + TclInvalidateNsCmdLookup(nsPtr); + + /* * Remove the hash entry for the command from the interpreter hidden * command table. */ if (cmdPtr->hPtr != NULL) { - Tcl_DeleteHashEntry(cmdPtr->hPtr); - cmdPtr->hPtr = NULL; + Tcl_DeleteHashEntry(cmdPtr->hPtr); + cmdPtr->hPtr = NULL; } /* - * Now link the hash table entry with the command structure. - * This is like creating a new command, so deal with any shadowing - * of commands in the global namespace. + * Now link the hash table entry with the command structure. This is like + * creating a new command, so deal with any shadowing of commands in the + * global namespace. */ - + cmdPtr->hPtr = hPtr; - Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); + Tcl_SetHashValue(hPtr, cmdPtr); /* - * Not needed as we are only in the global namespace - * (but would be needed again if we supported namespace command hiding) + * Not needed as we are only in the global namespace (but would be needed + * again if we supported namespace command hiding) * * TclResetShadowedCmdRefs(interp, cmdPtr); */ - /* - * If the command being exposed has a compile procedure, increment - * interpreter's compileEpoch to invalidate its compiled code. This - * makes sure that we don't later try to execute old code compiled - * assuming the command is hidden. This field is checked in Tcl_EvalObj - * and ObjInterpProc, and code whose compilation epoch doesn't match is + * If the command being exposed has a compile function, increment + * interpreter's compileEpoch to invalidate its compiled code. This makes + * sure that we don't later try to execute old code compiled assuming the + * command is hidden. This field is checked in Tcl_EvalObj and + * ObjInterpProc, and code whose compilation epoch doesn't match is * recompiled. */ @@ -1574,94 +1812,103 @@ Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName) * Define a new command in a command table. * * Results: - * The return value is a token for the command, which can - * be used in future calls to Tcl_GetCommandName. + * The return value is a token for the command, which can be used in + * future calls to Tcl_GetCommandName. * * Side effects: * If a command named cmdName already exists for interp, it is deleted. * In the future, when cmdName is seen as the name of a command by * Tcl_Eval, proc will be called. To support the bytecode interpreter, * the command is created with a wrapper Tcl_ObjCmdProc - * (TclInvokeStringCommand) that eventially calls proc. When the - * command is deleted from the table, deleteProc will be called. - * See the manual entry for details on the calling sequence. + * (TclInvokeStringCommand) that eventially calls proc. When the command + * is deleted from the table, deleteProc will be called. See the manual + * entry for details on the calling sequence. * *---------------------------------------------------------------------- */ Tcl_Command -Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) - Tcl_Interp *interp; /* Token for command interpreter returned by - * a previous call to Tcl_CreateInterp. */ - CONST char *cmdName; /* Name of command. If it contains namespace +Tcl_CreateCommand( + Tcl_Interp *interp, /* Token for command interpreter returned by a + * previous call to Tcl_CreateInterp. */ + const char *cmdName, /* Name of command. If it contains namespace * qualifiers, the new command is put in the - * specified namespace; otherwise it is put - * in the global namespace. */ - Tcl_CmdProc *proc; /* Procedure to associate with cmdName. */ - ClientData clientData; /* Arbitrary value passed to string proc. */ - Tcl_CmdDeleteProc *deleteProc; - /* If not NULL, gives a procedure to call - * when this command is deleted. */ + * specified namespace; otherwise it is put in + * the global namespace. */ + Tcl_CmdProc *proc, /* Function to associate with cmdName. */ + ClientData clientData, /* Arbitrary value passed to string proc. */ + Tcl_CmdDeleteProc *deleteProc) + /* If not NULL, gives a function to call when + * this command is deleted. */ { Interp *iPtr = (Interp *) interp; ImportRef *oldRefPtr = NULL; Namespace *nsPtr, *dummy1, *dummy2; Command *cmdPtr, *refCmdPtr; Tcl_HashEntry *hPtr; - CONST char *tail; - int new; + const char *tail; + int isNew; ImportedCmdData *dataPtr; if (iPtr->flags & DELETED) { /* - * The interpreter is being deleted. Don't create any new - * commands; it's not safe to muck with the interpreter anymore. + * The interpreter is being deleted. Don't create any new commands; + * it's not safe to muck with the interpreter anymore. */ return (Tcl_Command) NULL; } /* - * Determine where the command should reside. If its name contains - * namespace qualifiers, we put it in the specified namespace; - * otherwise, we always put it in the global namespace. + * Determine where the command should reside. If its name contains + * namespace qualifiers, we put it in the specified namespace; otherwise, + * we always put it in the global namespace. */ if (strstr(cmdName, "::") != NULL) { - TclGetNamespaceForQualName(interp, cmdName, (Namespace *) NULL, - CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); - if ((nsPtr == NULL) || (tail == NULL)) { + TclGetNamespaceForQualName(interp, cmdName, NULL, + TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); + if ((nsPtr == NULL) || (tail == NULL)) { return (Tcl_Command) NULL; } } else { nsPtr = iPtr->globalNsPtr; tail = cmdName; } - - hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); - if (!new) { + + hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); + if (!isNew) { /* - * Command already exists. Delete the old one. - * Be careful to preserve any existing import links so we can - * restore them down below. That way, you can redefine a - * command and its import status will remain intact. + * Command already exists. Delete the old one. Be careful to preserve + * any existing import links so we can restore them down below. That + * way, you can redefine a command and its import status will remain + * intact. */ - cmdPtr = (Command *) Tcl_GetHashValue(hPtr); + cmdPtr = Tcl_GetHashValue(hPtr); oldRefPtr = cmdPtr->importRefPtr; cmdPtr->importRefPtr = NULL; Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); - hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); - if (!new) { + hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); + if (!isNew) { /* - * If the deletion callback recreated the command, just throw - * away the new command (if we try to delete it again, we - * could get stuck in an infinite loop). + * If the deletion callback recreated the command, just throw away + * the new command (if we try to delete it again, we could get + * stuck in an infinite loop). */ - ckfree((char*) Tcl_GetHashValue(hPtr)); + ckfree((char *) Tcl_GetHashValue(hPtr)); } + } else { + /* + * The list of command exported from the namespace might have changed. + * However, we do not need to recompute this just yet; next time we + * need the info will be soon enough. + */ + + TclInvalidateNsCmdLookup(nsPtr); + TclInvalidateNsPath(nsPtr); } cmdPtr = (Command *) ckalloc(sizeof(Command)); Tcl_SetHashValue(hPtr, cmdPtr); @@ -1669,9 +1916,9 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) cmdPtr->nsPtr = nsPtr; cmdPtr->refCount = 1; cmdPtr->cmdEpoch = 0; - cmdPtr->compileProc = (CompileProc *) NULL; + cmdPtr->compileProc = NULL; cmdPtr->objProc = TclInvokeStringCommand; - cmdPtr->objClientData = (ClientData) cmdPtr; + cmdPtr->objClientData = cmdPtr; cmdPtr->proc = proc; cmdPtr->clientData = clientData; cmdPtr->deleteProc = deleteProc; @@ -1681,15 +1928,15 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) cmdPtr->tracePtr = NULL; /* - * Plug in any existing import references found above. Be sure - * to update all of these references to point to the new command. + * Plug in any existing import references found above. Be sure to update + * all of these references to point to the new command. */ if (oldRefPtr != NULL) { cmdPtr->importRefPtr = oldRefPtr; while (oldRefPtr != NULL) { refCmdPtr = oldRefPtr->importedCmdPtr; - dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; + dataPtr = refCmdPtr->objClientData; dataPtr->realCmdPtr = cmdPtr; oldRefPtr = oldRefPtr->nextPtr; } @@ -1701,7 +1948,7 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) * shadowed commands are found, invalidate all cached command references * in the affected namespaces. */ - + TclResetShadowedCmdRefs(interp, cmdPtr); return (Tcl_Command) cmdPtr; } @@ -1714,70 +1961,70 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) * Define a new object-based command in a command table. * * Results: - * The return value is a token for the command, which can - * be used in future calls to Tcl_GetCommandName. + * The return value is a token for the command, which can be used in + * future calls to Tcl_GetCommandName. * * Side effects: * If no command named "cmdName" already exists for interp, one is - * created. Otherwise, if a command does exist, then if the - * object-based Tcl_ObjCmdProc is TclInvokeStringCommand, we assume - * Tcl_CreateCommand was called previously for the same command and - * just set its Tcl_ObjCmdProc to the argument "proc"; otherwise, we - * delete the old command. + * created. Otherwise, if a command does exist, then if the object-based + * Tcl_ObjCmdProc is TclInvokeStringCommand, we assume Tcl_CreateCommand + * was called previously for the same command and just set its + * Tcl_ObjCmdProc to the argument "proc"; otherwise, we delete the old + * command. * * In the future, during bytecode evaluation when "cmdName" is seen as * the name of a command by Tcl_EvalObj or Tcl_Eval, the object-based * Tcl_ObjCmdProc proc will be called. When the command is deleted from - * the table, deleteProc will be called. See the manual entry for - * details on the calling sequence. + * the table, deleteProc will be called. See the manual entry for details + * on the calling sequence. * *---------------------------------------------------------------------- */ Tcl_Command -Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) - Tcl_Interp *interp; /* Token for command interpreter (returned - * by previous call to Tcl_CreateInterp). */ - CONST char *cmdName; /* Name of command. If it contains namespace +Tcl_CreateObjCommand( + Tcl_Interp *interp, /* Token for command interpreter (returned by + * previous call to Tcl_CreateInterp). */ + const char *cmdName, /* Name of command. If it contains namespace * qualifiers, the new command is put in the - * specified namespace; otherwise it is put - * in the global namespace. */ - Tcl_ObjCmdProc *proc; /* Object-based procedure to associate with + * specified namespace; otherwise it is put in + * the global namespace. */ + Tcl_ObjCmdProc *proc, /* Object-based function to associate with * name. */ - ClientData clientData; /* Arbitrary value to pass to object - * procedure. */ - Tcl_CmdDeleteProc *deleteProc; - /* If not NULL, gives a procedure to call - * when this command is deleted. */ + ClientData clientData, /* Arbitrary value to pass to object + * function. */ + Tcl_CmdDeleteProc *deleteProc) + /* If not NULL, gives a function to call when + * this command is deleted. */ { Interp *iPtr = (Interp *) interp; ImportRef *oldRefPtr = NULL; Namespace *nsPtr, *dummy1, *dummy2; Command *cmdPtr, *refCmdPtr; Tcl_HashEntry *hPtr; - CONST char *tail; - int new; + const char *tail; + int isNew; ImportedCmdData *dataPtr; if (iPtr->flags & DELETED) { /* - * The interpreter is being deleted. Don't create any new - * commands; it's not safe to muck with the interpreter anymore. + * The interpreter is being deleted. Don't create any new commands; + * it's not safe to muck with the interpreter anymore. */ return (Tcl_Command) NULL; } /* - * Determine where the command should reside. If its name contains - * namespace qualifiers, we put it in the specified namespace; - * otherwise, we always put it in the global namespace. + * Determine where the command should reside. If its name contains + * namespace qualifiers, we put it in the specified namespace; otherwise, + * we always put it in the global namespace. */ if (strstr(cmdName, "::") != NULL) { - TclGetNamespaceForQualName(interp, cmdName, (Namespace *) NULL, - CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); - if ((nsPtr == NULL) || (tail == NULL)) { + TclGetNamespaceForQualName(interp, cmdName, NULL, + TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); + if ((nsPtr == NULL) || (tail == NULL)) { return (Tcl_Command) NULL; } } else { @@ -1785,45 +2032,54 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) tail = cmdName; } - hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); - if (!new) { - cmdPtr = (Command *) Tcl_GetHashValue(hPtr); + hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); + TclInvalidateNsPath(nsPtr); + if (!isNew) { + cmdPtr = Tcl_GetHashValue(hPtr); /* * Command already exists. If its object-based Tcl_ObjCmdProc is * TclInvokeStringCommand, we just set its Tcl_ObjCmdProc to the - * argument "proc". Otherwise, we delete the old command. + * argument "proc". Otherwise, we delete the old command. */ if (cmdPtr->objProc == TclInvokeStringCommand) { cmdPtr->objProc = proc; cmdPtr->objClientData = clientData; - cmdPtr->deleteProc = deleteProc; - cmdPtr->deleteData = clientData; + cmdPtr->deleteProc = deleteProc; + cmdPtr->deleteData = clientData; return (Tcl_Command) cmdPtr; } /* - * Otherwise, we delete the old command. Be careful to preserve - * any existing import links so we can restore them down below. - * That way, you can redefine a command and its import status - * will remain intact. + * Otherwise, we delete the old command. Be careful to preserve any + * existing import links so we can restore them down below. That way, + * you can redefine a command and its import status will remain + * intact. */ oldRefPtr = cmdPtr->importRefPtr; cmdPtr->importRefPtr = NULL; Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); - hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); - if (!new) { + hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); + if (!isNew) { /* - * If the deletion callback recreated the command, just throw - * away the new command (if we try to delete it again, we - * could get stuck in an infinite loop). + * If the deletion callback recreated the command, just throw away + * the new command (if we try to delete it again, we could get + * stuck in an infinite loop). */ - ckfree((char *) Tcl_GetHashValue(hPtr)); + ckfree(Tcl_GetHashValue(hPtr)); } + } else { + /* + * The list of command exported from the namespace might have changed. + * However, we do not need to recompute this just yet; next time we + * need the info will be soon enough. + */ + + TclInvalidateNsCmdLookup(nsPtr); } cmdPtr = (Command *) ckalloc(sizeof(Command)); Tcl_SetHashValue(hPtr, cmdPtr); @@ -1831,11 +2087,11 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) cmdPtr->nsPtr = nsPtr; cmdPtr->refCount = 1; cmdPtr->cmdEpoch = 0; - cmdPtr->compileProc = (CompileProc *) NULL; + cmdPtr->compileProc = NULL; cmdPtr->objProc = proc; cmdPtr->objClientData = clientData; cmdPtr->proc = TclInvokeObjectCommand; - cmdPtr->clientData = (ClientData) cmdPtr; + cmdPtr->clientData = cmdPtr; cmdPtr->deleteProc = deleteProc; cmdPtr->deleteData = clientData; cmdPtr->flags = 0; @@ -1843,27 +2099,27 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) cmdPtr->tracePtr = NULL; /* - * Plug in any existing import references found above. Be sure - * to update all of these references to point to the new command. + * Plug in any existing import references found above. Be sure to update + * all of these references to point to the new command. */ if (oldRefPtr != NULL) { cmdPtr->importRefPtr = oldRefPtr; while (oldRefPtr != NULL) { refCmdPtr = oldRefPtr->importedCmdPtr; - dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; + dataPtr = refCmdPtr->objClientData; dataPtr->realCmdPtr = cmdPtr; oldRefPtr = oldRefPtr->nextPtr; } } - + /* * We just created a command, so in its namespace and all of its parent * namespaces, it may shadow global commands with the same name. If any * shadowed commands are found, invalidate all cached command references * in the affected namespaces. */ - + TclResetShadowedCmdRefs(interp, cmdPtr); return (Tcl_Command) cmdPtr; } @@ -1874,10 +2130,10 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) * TclInvokeStringCommand -- * * "Wrapper" Tcl_ObjCmdProc used to call an existing string-based - * Tcl_CmdProc if no object-based procedure exists for a command. A - * pointer to this procedure is stored as the Tcl_ObjCmdProc in a - * Command structure. It simply turns around and calls the string - * Tcl_CmdProc in the Command structure. + * Tcl_CmdProc if no object-based function exists for a command. A + * pointer to this function is stored as the Tcl_ObjCmdProc in a Command + * structure. It simply turns around and calls the string Tcl_CmdProc in + * the Command structure. * * Results: * A standard Tcl object result value. @@ -1890,35 +2146,16 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) */ int -TclInvokeStringCommand(clientData, interp, objc, objv) - ClientData clientData; /* Points to command's Command structure. */ - Tcl_Interp *interp; /* Current interpreter. */ - register int objc; /* Number of arguments. */ - Tcl_Obj *CONST objv[]; /* Argument objects. */ +TclInvokeStringCommand( + ClientData clientData, /* Points to command's Command structure. */ + Tcl_Interp *interp, /* Current interpreter. */ + register int objc, /* Number of arguments. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { - register Command *cmdPtr = (Command *) clientData; - register int i; - int result; - - /* - * This procedure generates an argv array for the string arguments. It - * starts out with stack-allocated space but uses dynamically-allocated - * storage if needed. - */ - -#define NUM_ARGS 20 - CONST char *(argStorage[NUM_ARGS]); - CONST char **argv = argStorage; - - /* - * Create the string argument array "argv". Make sure argv is large - * enough to hold the objc arguments plus 1 extra for the zero - * end-of-argv word. - */ - - if ((objc + 1) > NUM_ARGS) { - argv = (CONST char **) ckalloc((unsigned)(objc + 1) * sizeof(char *)); - } + Command *cmdPtr = clientData; + int i, result; + const char **argv = (const char **) + TclStackAlloc(interp, (unsigned)(objc + 1) * sizeof(char *)); for (i = 0; i < objc; i++) { argv[i] = Tcl_GetString(objv[i]); @@ -1931,15 +2168,8 @@ TclInvokeStringCommand(clientData, interp, objc, objv) result = (*cmdPtr->proc)(cmdPtr->clientData, interp, objc, argv); - /* - * Free the argv array if malloc'ed storage was used. - */ - - if (argv != argStorage) { - ckfree((char *) argv); - } + TclStackFree(interp, (void *) argv); return result; -#undef NUM_ARGS } /* @@ -1948,10 +2178,10 @@ TclInvokeStringCommand(clientData, interp, objc, objv) * TclInvokeObjectCommand -- * * "Wrapper" Tcl_CmdProc used to call an existing object-based - * Tcl_ObjCmdProc if no string-based procedure exists for a command. - * A pointer to this procedure is stored as the Tcl_CmdProc in a - * Command structure. It simply turns around and calls the object - * Tcl_ObjCmdProc in the Command structure. + * Tcl_ObjCmdProc if no string-based function exists for a command. A + * pointer to this function is stored as the Tcl_CmdProc in a Command + * structure. It simply turns around and calls the object Tcl_ObjCmdProc + * in the Command structure. * * Results: * A standard Tcl string result value. @@ -1964,42 +2194,21 @@ TclInvokeStringCommand(clientData, interp, objc, objv) */ int -TclInvokeObjectCommand(clientData, interp, argc, argv) - ClientData clientData; /* Points to command's Command structure. */ - Tcl_Interp *interp; /* Current interpreter. */ - int argc; /* Number of arguments. */ - register CONST char **argv; /* Argument strings. */ +TclInvokeObjectCommand( + ClientData clientData, /* Points to command's Command structure. */ + Tcl_Interp *interp, /* Current interpreter. */ + int argc, /* Number of arguments. */ + register const char **argv) /* Argument strings. */ { Command *cmdPtr = (Command *) clientData; - register Tcl_Obj *objPtr; - register int i; - int length, result; - - /* - * This procedure generates an objv array for object arguments that hold - * the argv strings. It starts out with stack-allocated space but uses - * dynamically-allocated storage if needed. - */ - -#define NUM_ARGS 20 - Tcl_Obj *(argStorage[NUM_ARGS]); - register Tcl_Obj **objv = argStorage; - - /* - * Create the object argument array "objv". Make sure objv is large - * enough to hold the objc arguments plus 1 extra for the zero - * end-of-objv word. - */ - - if (argc > NUM_ARGS) { - objv = (Tcl_Obj **) - ckalloc((unsigned)(argc * sizeof(Tcl_Obj *))); - } + Tcl_Obj *objPtr; + int i, length, result; + Tcl_Obj **objv = (Tcl_Obj **) + TclStackAlloc(interp, (unsigned)(argc * sizeof(Tcl_Obj *))); for (i = 0; i < argc; i++) { length = strlen(argv[i]); - TclNewObj(objPtr); - TclInitStringRep(objPtr, argv[i], length); + TclNewStringObj(objPtr, argv[i], length); Tcl_IncrRefCount(objPtr); objv[i] = objPtr; } @@ -2011,27 +2220,23 @@ TclInvokeObjectCommand(clientData, interp, argc, argv) result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, argc, objv); /* - * Move the interpreter's object result to the string result, - * then reset the object result. + * Move the interpreter's object result to the string result, then reset + * the object result. */ - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); - + (void) Tcl_GetStringResult(interp); + /* - * Decrement the ref counts for the argument objects created above, - * then free the objv array if malloc'ed storage was used. + * Decrement the ref counts for the argument objects created above, then + * free the objv array if malloc'ed storage was used. */ for (i = 0; i < argc; i++) { objPtr = objv[i]; Tcl_DecrRefCount(objPtr); } - if (objv != argStorage) { - ckfree((char *) objv); - } + TclStackFree(interp, objv); return result; -#undef NUM_ARGS } /* @@ -2039,65 +2244,64 @@ TclInvokeObjectCommand(clientData, interp, argc, argv) * * TclRenameCommand -- * - * Called to give an existing Tcl command a different name. Both the - * old command name and the new command name can have "::" namespace - * qualifiers. If the new command has a different namespace context, - * the command will be moved to that namespace and will execute in - * the context of that new namespace. + * Called to give an existing Tcl command a different name. Both the old + * command name and the new command name can have "::" namespace + * qualifiers. If the new command has a different namespace context, the + * command will be moved to that namespace and will execute in the + * context of that new namespace. * - * If the new command name is NULL or the null string, the command is - * deleted. + * If the new command name is NULL or the null string, the command is + * deleted. * * Results: - * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + * Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. * * Side effects: - * If anything goes wrong, an error message is returned in the - * interpreter's result object. + * If anything goes wrong, an error message is returned in the + * interpreter's result object. * *---------------------------------------------------------------------- */ int -TclRenameCommand(interp, oldName, newName) - Tcl_Interp *interp; /* Current interpreter. */ - char *oldName; /* Existing command name. */ - char *newName; /* New command name. */ +TclRenameCommand( + Tcl_Interp *interp, /* Current interpreter. */ + const char *oldName, /* Existing command name. */ + const char *newName) /* New command name. */ { Interp *iPtr = (Interp *) interp; - CONST char *newTail; + const char *newTail; Namespace *cmdNsPtr, *newNsPtr, *dummy1, *dummy2; Tcl_Command cmd; Command *cmdPtr; Tcl_HashEntry *hPtr, *oldHPtr; - int new, result; - Tcl_Obj* oldFullName; + int isNew, result; + Tcl_Obj *oldFullName; Tcl_DString newFullName; /* - * Find the existing command. An error is returned if cmdName can't - * be found. + * Find the existing command. An error is returned if cmdName can't be + * found. */ - cmd = Tcl_FindCommand(interp, oldName, (Tcl_Namespace *) NULL, - /*flags*/ 0); + cmd = Tcl_FindCommand(interp, oldName, NULL, /*flags*/ 0); cmdPtr = (Command *) cmd; if (cmdPtr == NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "can't ", - ((newName == NULL)||(*newName == '\0'))? "delete":"rename", - " \"", oldName, "\": command doesn't exist", (char *) NULL); + Tcl_AppendResult(interp, "can't ", + ((newName == NULL)||(*newName == '\0'))? "delete":"rename", + " \"", oldName, "\": command doesn't exist", NULL); return TCL_ERROR; } cmdNsPtr = cmdPtr->nsPtr; oldFullName = Tcl_NewObj(); - Tcl_IncrRefCount( oldFullName ); - Tcl_GetCommandFullName( interp, cmd, oldFullName ); + Tcl_IncrRefCount(oldFullName); + Tcl_GetCommandFullName(interp, cmd, oldFullName); /* * If the new command name is NULL or empty, delete the command. Do this * with Tcl_DeleteCommandFromToken, since we already have the command. */ - + if ((newName == NULL) || (*newName == '\0')) { Tcl_DeleteCommandFromToken(interp, cmd); result = TCL_OK; @@ -2105,101 +2309,106 @@ TclRenameCommand(interp, oldName, newName) } /* - * Make sure that the destination command does not already exist. - * The rename operation is like creating a command, so we should - * automatically create the containing namespaces just like - * Tcl_CreateCommand would. + * Make sure that the destination command does not already exist. The + * rename operation is like creating a command, so we should automatically + * create the containing namespaces just like Tcl_CreateCommand would. */ - TclGetNamespaceForQualName(interp, newName, (Namespace *) NULL, - CREATE_NS_IF_UNKNOWN, &newNsPtr, &dummy1, &dummy2, &newTail); + TclGetNamespaceForQualName(interp, newName, NULL, + TCL_CREATE_NS_IF_UNKNOWN, &newNsPtr, &dummy1, &dummy2, &newTail); if ((newNsPtr == NULL) || (newTail == NULL)) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "can't rename to \"", newName, "\": bad command name", - (char *) NULL); + Tcl_AppendResult(interp, "can't rename to \"", newName, + "\": bad command name", NULL); result = TCL_ERROR; goto done; } if (Tcl_FindHashEntry(&newNsPtr->cmdTable, newTail) != NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "can't rename to \"", newName, - "\": command already exists", (char *) NULL); + Tcl_AppendResult(interp, "can't rename to \"", newName, + "\": command already exists", NULL); result = TCL_ERROR; goto done; } /* - * Warning: any changes done in the code here are likely - * to be needed in Tcl_HideCommand() code too. - * (until the common parts are extracted out) --dl + * Warning: any changes done in the code here are likely to be needed in + * Tcl_HideCommand() code too (until the common parts are extracted out). + * - dl */ /* - * Put the command in the new namespace so we can check for an alias - * loop. Since we are adding a new command to a namespace, we must - * handle any shadowing of the global commands that this might create. + * Put the command in the new namespace so we can check for an alias loop. + * Since we are adding a new command to a namespace, we must handle any + * shadowing of the global commands that this might create. */ - + oldHPtr = cmdPtr->hPtr; - hPtr = Tcl_CreateHashEntry(&newNsPtr->cmdTable, newTail, &new); - Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); + hPtr = Tcl_CreateHashEntry(&newNsPtr->cmdTable, newTail, &isNew); + Tcl_SetHashValue(hPtr, cmdPtr); cmdPtr->hPtr = hPtr; cmdPtr->nsPtr = newNsPtr; TclResetShadowedCmdRefs(interp, cmdPtr); /* - * Now check for an alias loop. If we detect one, put everything back - * the way it was and report the error. + * Now check for an alias loop. If we detect one, put everything back the + * way it was and report the error. */ result = TclPreventAliasLoop(interp, interp, (Tcl_Command) cmdPtr); if (result != TCL_OK) { - Tcl_DeleteHashEntry(cmdPtr->hPtr); - cmdPtr->hPtr = oldHPtr; - cmdPtr->nsPtr = cmdNsPtr; + Tcl_DeleteHashEntry(cmdPtr->hPtr); + cmdPtr->hPtr = oldHPtr; + cmdPtr->nsPtr = cmdNsPtr; goto done; } /* - * Script for rename traces can delete the command "oldName". - * Therefore increment the reference count for cmdPtr so that - * it's Command structure is freed only towards the end of this - * function by calling TclCleanupCommand. + * The list of command exported from the namespace might have changed. + * However, we do not need to recompute this just yet; next time we need + * the info will be soon enough. These might refer to the same variable, + * but that's no big deal. + */ + + TclInvalidateNsCmdLookup(cmdNsPtr); + TclInvalidateNsCmdLookup(cmdPtr->nsPtr); + + /* + * Script for rename traces can delete the command "oldName". Therefore + * increment the reference count for cmdPtr so that it's Command structure + * is freed only towards the end of this function by calling + * TclCleanupCommand. * - * The trace procedure needs to get a fully qualified name for - * old and new commands [Tcl bug #651271], or else there's no way - * for the trace procedure to get the namespace from which the old - * command is being renamed! + * The trace function needs to get a fully qualified name for old and new + * commands [Tcl bug #651271], or else there's no way for the trace + * function to get the namespace from which the old command is being + * renamed! */ - Tcl_DStringInit( &newFullName ); - Tcl_DStringAppend( &newFullName, newNsPtr->fullName, -1 ); - if ( newNsPtr != iPtr->globalNsPtr ) { - Tcl_DStringAppend( &newFullName, "::", 2 ); + Tcl_DStringInit(&newFullName); + Tcl_DStringAppend(&newFullName, newNsPtr->fullName, -1); + if (newNsPtr != iPtr->globalNsPtr) { + Tcl_DStringAppend(&newFullName, "::", 2); } - Tcl_DStringAppend( &newFullName, newTail, -1 ); + Tcl_DStringAppend(&newFullName, newTail, -1); cmdPtr->refCount++; - CallCommandTraces( iPtr, cmdPtr, - Tcl_GetString( oldFullName ), - Tcl_DStringValue( &newFullName ), - TCL_TRACE_RENAME); - Tcl_DStringFree( &newFullName ); + CallCommandTraces(iPtr, cmdPtr, Tcl_GetString(oldFullName), + Tcl_DStringValue(&newFullName), TCL_TRACE_RENAME); + Tcl_DStringFree(&newFullName); /* - * The new command name is okay, so remove the command from its - * current namespace. This is like deleting the command, so bump - * the cmdEpoch to invalidate any cached references to the command. + * The new command name is okay, so remove the command from its current + * namespace. This is like deleting the command, so bump the cmdEpoch to + * invalidate any cached references to the command. */ - + Tcl_DeleteHashEntry(oldHPtr); cmdPtr->cmdEpoch++; /* - * If the command being renamed has a compile procedure, increment the - * interpreter's compileEpoch to invalidate its compiled code. This - * makes sure that we don't later try to execute old code compiled for - * the now-renamed command. + * If the command being renamed has a compile function, increment the + * interpreter's compileEpoch to invalidate its compiled code. This makes + * sure that we don't later try to execute old code compiled for the + * now-renamed command. */ if (cmdPtr->compileProc != NULL) { @@ -2207,14 +2416,15 @@ TclRenameCommand(interp, oldName, newName) } /* - * Now free the Command structure, if the "oldName" command has - * been deleted by invocation of rename traces. + * Now free the Command structure, if the "oldName" command has been + * deleted by invocation of rename traces. */ - TclCleanupCommand(cmdPtr); + + TclCleanupCommandMacro(cmdPtr); result = TCL_OK; - done: - TclDecrRefCount( oldFullName ); + done: + TclDecrRefCount(oldFullName); return result; } @@ -2223,16 +2433,15 @@ TclRenameCommand(interp, oldName, newName) * * Tcl_SetCommandInfo -- * - * Modifies various information about a Tcl command. Note that - * this procedure will not change a command's namespace; use - * Tcl_RenameCommand to do that. Also, the isNativeObjectProc - * member of *infoPtr is ignored. + * Modifies various information about a Tcl command. Note that this + * function will not change a command's namespace; use TclRenameCommand + * to do that. Also, the isNativeObjectProc member of *infoPtr is + * ignored. * * Results: - * If cmdName exists in interp, then the information at *infoPtr - * is stored with the command in place of the current information - * and 1 is returned. If the command doesn't exist then 0 is - * returned. + * If cmdName exists in interp, then the information at *infoPtr is + * stored with the command in place of the current information and 1 is + * returned. If the command doesn't exist then 0 is returned. * * Side effects: * None. @@ -2241,20 +2450,17 @@ TclRenameCommand(interp, oldName, newName) */ int -Tcl_SetCommandInfo(interp, cmdName, infoPtr) - Tcl_Interp *interp; /* Interpreter in which to look - * for command. */ - CONST char *cmdName; /* Name of desired command. */ - CONST Tcl_CmdInfo *infoPtr; /* Where to find information - * to store in the command. */ +Tcl_SetCommandInfo( + Tcl_Interp *interp, /* Interpreter in which to look for + * command. */ + const char *cmdName, /* Name of desired command. */ + const Tcl_CmdInfo *infoPtr) /* Where to find information to store in the + * command. */ { Tcl_Command cmd; - cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, - /*flags*/ 0); - - return Tcl_SetCommandInfoFromToken( cmd, infoPtr ); - + cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); + return Tcl_SetCommandInfoFromToken(cmd, infoPtr); } /* @@ -2262,16 +2468,15 @@ Tcl_SetCommandInfo(interp, cmdName, infoPtr) * * Tcl_SetCommandInfoFromToken -- * - * Modifies various information about a Tcl command. Note that - * this procedure will not change a command's namespace; use - * Tcl_RenameCommand to do that. Also, the isNativeObjectProc - * member of *infoPtr is ignored. + * Modifies various information about a Tcl command. Note that this + * function will not change a command's namespace; use TclRenameCommand + * to do that. Also, the isNativeObjectProc member of *infoPtr is + * ignored. * * Results: - * If cmdName exists in interp, then the information at *infoPtr - * is stored with the command in place of the current information - * and 1 is returned. If the command doesn't exist then 0 is - * returned. + * If cmdName exists in interp, then the information at *infoPtr is + * stored with the command in place of the current information and 1 is + * returned. If the command doesn't exist then 0 is returned. * * Side effects: * None. @@ -2280,11 +2485,11 @@ Tcl_SetCommandInfo(interp, cmdName, infoPtr) */ int -Tcl_SetCommandInfoFromToken( cmd, infoPtr ) - Tcl_Command cmd; - CONST Tcl_CmdInfo* infoPtr; +Tcl_SetCommandInfoFromToken( + Tcl_Command cmd, + const Tcl_CmdInfo *infoPtr) { - Command* cmdPtr; /* Internal representation of the command */ + Command *cmdPtr; /* Internal representation of the command */ if (cmd == (Tcl_Command) NULL) { return 0; @@ -2293,13 +2498,13 @@ Tcl_SetCommandInfoFromToken( cmd, infoPtr ) /* * The isNativeObjectProc and nsPtr members of *infoPtr are ignored. */ - + cmdPtr = (Command *) cmd; cmdPtr->proc = infoPtr->proc; cmdPtr->clientData = infoPtr->clientData; - if (infoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { + if (infoPtr->objProc == NULL) { cmdPtr->objProc = TclInvokeStringCommand; - cmdPtr->objClientData = (ClientData) cmdPtr; + cmdPtr->objClientData = cmdPtr; } else { cmdPtr->objProc = infoPtr->objProc; cmdPtr->objClientData = infoPtr->objClientData; @@ -2317,10 +2522,9 @@ Tcl_SetCommandInfoFromToken( cmd, infoPtr ) * Returns various information about a Tcl command. * * Results: - * If cmdName exists in interp, then *infoPtr is modified to - * hold information about cmdName and 1 is returned. If the - * command doesn't exist then 0 is returned and *infoPtr isn't - * modified. + * If cmdName exists in interp, then *infoPtr is modified to hold + * information about cmdName and 1 is returned. If the command doesn't + * exist then 0 is returned and *infoPtr isn't modified. * * Side effects: * None. @@ -2329,20 +2533,17 @@ Tcl_SetCommandInfoFromToken( cmd, infoPtr ) */ int -Tcl_GetCommandInfo(interp, cmdName, infoPtr) - Tcl_Interp *interp; /* Interpreter in which to look - * for command. */ - CONST char *cmdName; /* Name of desired command. */ - Tcl_CmdInfo *infoPtr; /* Where to store information about - * command. */ +Tcl_GetCommandInfo( + Tcl_Interp *interp, /* Interpreter in which to look for + * command. */ + const char *cmdName, /* Name of desired command. */ + Tcl_CmdInfo *infoPtr) /* Where to store information about + * command. */ { Tcl_Command cmd; - cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, - /*flags*/ 0); - - return Tcl_GetCommandInfoFromToken( cmd, infoPtr ); - + cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); + return Tcl_GetCommandInfoFromToken(cmd, infoPtr); } /* @@ -2353,9 +2554,9 @@ Tcl_GetCommandInfo(interp, cmdName, infoPtr) * Returns various information about a Tcl command. * * Results: - * Copies information from the command identified by 'cmd' into - * a caller-supplied structure and returns 1. If the 'cmd' is - * NULL, leaves the structure untouched and returns 0. + * Copies information from the command identified by 'cmd' into a + * caller-supplied structure and returns 1. If the 'cmd' is NULL, leaves + * the structure untouched and returns 0. * * Side effects: * None. @@ -2364,14 +2565,13 @@ Tcl_GetCommandInfo(interp, cmdName, infoPtr) */ int -Tcl_GetCommandInfoFromToken( cmd, infoPtr ) - Tcl_Command cmd; - Tcl_CmdInfo* infoPtr; +Tcl_GetCommandInfoFromToken( + Tcl_Command cmd, + Tcl_CmdInfo *infoPtr) { + Command *cmdPtr; /* Internal representation of the command */ - Command* cmdPtr; /* Internal representation of the command */ - - if ( cmd == (Tcl_Command) NULL ) { + if (cmd == (Tcl_Command) NULL) { return 0; } @@ -2392,7 +2592,6 @@ Tcl_GetCommandInfoFromToken( cmd, infoPtr ) infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr; return 1; - } /* @@ -2400,9 +2599,8 @@ Tcl_GetCommandInfoFromToken( cmd, infoPtr ) * * Tcl_GetCommandName -- * - * Given a token returned by Tcl_CreateCommand, this procedure - * returns the current name of the command (which may have changed - * due to renaming). + * Given a token returned by Tcl_CreateCommand, this function returns the + * current name of the command (which may have changed due to renaming). * * Results: * The return value is the name of the given command. @@ -2413,25 +2611,25 @@ Tcl_GetCommandInfoFromToken( cmd, infoPtr ) *---------------------------------------------------------------------- */ -CONST char * -Tcl_GetCommandName(interp, command) - Tcl_Interp *interp; /* Interpreter containing the command. */ - Tcl_Command command; /* Token for command returned by a previous - * call to Tcl_CreateCommand. The command - * must not have been deleted. */ +const char * +Tcl_GetCommandName( + Tcl_Interp *interp, /* Interpreter containing the command. */ + Tcl_Command command) /* Token for command returned by a previous + * call to Tcl_CreateCommand. The command must + * not have been deleted. */ { Command *cmdPtr = (Command *) command; if ((cmdPtr == NULL) || (cmdPtr->hPtr == NULL)) { - /* * This should only happen if command was "created" after the - * interpreter began to be deleted, so there isn't really any - * command. Just return an empty string. + * interpreter began to be deleted, so there isn't really any command. + * Just return an empty string. */ return ""; } + return Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); } @@ -2440,28 +2638,28 @@ Tcl_GetCommandName(interp, command) * * Tcl_GetCommandFullName -- * - * Given a token returned by, e.g., Tcl_CreateCommand or - * Tcl_FindCommand, this procedure appends to an object the command's - * full name, qualified by a sequence of parent namespace names. The - * command's fully-qualified name may have changed due to renaming. + * Given a token returned by, e.g., Tcl_CreateCommand or Tcl_FindCommand, + * this function appends to an object the command's full name, qualified + * by a sequence of parent namespace names. The command's fully-qualified + * name may have changed due to renaming. * * Results: * None. * * Side effects: * The command's fully-qualified name is appended to the string - * representation of objPtr. + * representation of objPtr. * *---------------------------------------------------------------------- */ void -Tcl_GetCommandFullName(interp, command, objPtr) - Tcl_Interp *interp; /* Interpreter containing the command. */ - Tcl_Command command; /* Token for command returned by a previous - * call to Tcl_CreateCommand. The command - * must not have been deleted. */ - Tcl_Obj *objPtr; /* Points to the object onto which the +Tcl_GetCommandFullName( + Tcl_Interp *interp, /* Interpreter containing the command. */ + Tcl_Command command, /* Token for command returned by a previous + * call to Tcl_CreateCommand. The command must + * not have been deleted. */ + Tcl_Obj *objPtr) /* Points to the object onto which the * command's full name is appended. */ { @@ -2484,7 +2682,7 @@ Tcl_GetCommandFullName(interp, command, objPtr) if (cmdPtr->hPtr != NULL) { name = Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); Tcl_AppendToObj(objPtr, name, -1); - } + } } } @@ -2496,30 +2694,28 @@ Tcl_GetCommandFullName(interp, command, objPtr) * Remove the given command from the given interpreter. * * Results: - * 0 is returned if the command was deleted successfully. - * -1 is returned if there didn't exist a command by that name. + * 0 is returned if the command was deleted successfully. -1 is returned + * if there didn't exist a command by that name. * * Side effects: - * cmdName will no longer be recognized as a valid command for - * interp. + * cmdName will no longer be recognized as a valid command for interp. * *---------------------------------------------------------------------- */ int -Tcl_DeleteCommand(interp, cmdName) - Tcl_Interp *interp; /* Token for command interpreter (returned - * by a previous Tcl_CreateInterp call). */ - CONST char *cmdName; /* Name of command to remove. */ +Tcl_DeleteCommand( + Tcl_Interp *interp, /* Token for command interpreter (returned by + * a previous Tcl_CreateInterp call). */ + const char *cmdName) /* Name of command to remove. */ { Tcl_Command cmd; /* - * Find the desired command and delete it. + * Find the desired command and delete it. */ - cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, - /*flags*/ 0); + cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); if (cmd == (Tcl_Command) NULL) { return -1; } @@ -2531,26 +2727,26 @@ Tcl_DeleteCommand(interp, cmdName) * * Tcl_DeleteCommandFromToken -- * - * Removes the given command from the given interpreter. This procedure - * resembles Tcl_DeleteCommand, but takes a Tcl_Command token instead - * of a command name for efficiency. + * Removes the given command from the given interpreter. This function + * resembles Tcl_DeleteCommand, but takes a Tcl_Command token instead of + * a command name for efficiency. * * Results: - * 0 is returned if the command was deleted successfully. - * -1 is returned if there didn't exist a command by that name. + * 0 is returned if the command was deleted successfully. -1 is returned + * if there didn't exist a command by that name. * * Side effects: - * The command specified by "cmd" will no longer be recognized as a - * valid command for "interp". + * The command specified by "cmd" will no longer be recognized as a valid + * command for "interp". * *---------------------------------------------------------------------- */ int -Tcl_DeleteCommandFromToken(interp, cmd) - Tcl_Interp *interp; /* Token for command interpreter returned by - * a previous call to Tcl_CreateInterp. */ - Tcl_Command cmd; /* Token for command to delete. */ +Tcl_DeleteCommandFromToken( + Tcl_Interp *interp, /* Token for command interpreter returned by a + * previous call to Tcl_CreateInterp. */ + Tcl_Command cmd) /* Token for command to delete. */ { Interp *iPtr = (Interp *) interp; Command *cmdPtr = (Command *) cmd; @@ -2558,73 +2754,90 @@ Tcl_DeleteCommandFromToken(interp, cmd) Tcl_Command importCmd; /* - * The code here is tricky. We can't delete the hash table entry - * before invoking the deletion callback because there are cases - * where the deletion callback needs to invoke the command (e.g. - * object systems such as OTcl). However, this means that the - * callback could try to delete or rename the command. The deleted - * flag allows us to detect these cases and skip nested deletes. + * Bump the command epoch counter. This will invalidate all cached + * references that point to this command. + */ + + cmdPtr->cmdEpoch++; + + /* + * The code here is tricky. We can't delete the hash table entry before + * invoking the deletion callback because there are cases where the + * deletion callback needs to invoke the command (e.g. object systems such + * as OTcl). However, this means that the callback could try to delete or + * rename the command. The deleted flag allows us to detect these cases + * and skip nested deletes. */ if (cmdPtr->flags & CMD_IS_DELETED) { /* - * Another deletion is already in progress. Remove the hash - * table entry now, but don't invoke a callback or free the - * command structure. + * Another deletion is already in progress. Remove the hash table + * entry now, but don't invoke a callback or free the command + * structure. Take care to only remove the hash entry if it has not + * already been removed; otherwise if we manage to hit this function + * three times, everything goes up in smoke. [Bug 1220058] */ - Tcl_DeleteHashEntry(cmdPtr->hPtr); - cmdPtr->hPtr = NULL; + if (cmdPtr->hPtr != NULL) { + Tcl_DeleteHashEntry(cmdPtr->hPtr); + cmdPtr->hPtr = NULL; + } return 0; } - /* - * We must delete this command, even though both traces and - * delete procs may try to avoid this (renaming the command etc). - * Also traces and delete procs may try to delete the command - * themsevles. This flag declares that a delete is in progress - * and that recursive deletes should be ignored. - */ - cmdPtr->flags |= CMD_IS_DELETED; - /* - * Bump the command epoch counter. This will invalidate all cached - * references that point to this command. + * We must delete this command, even though both traces and delete procs + * may try to avoid this (renaming the command etc). Also traces and + * delete procs may try to delete the command themsevles. This flag + * declares that a delete is in progress and that recursive deletes should + * be ignored. */ - - cmdPtr->cmdEpoch++; + + cmdPtr->flags |= CMD_IS_DELETED; /* - * Call trace procedures for the command being deleted. Then delete - * its traces. + * Call trace functions for the command being deleted. Then delete its + * traces. */ if (cmdPtr->tracePtr != NULL) { CommandTrace *tracePtr; CallCommandTraces(iPtr,cmdPtr,NULL,NULL,TCL_TRACE_DELETE); - /* Now delete these traces */ + + /* + * Now delete these traces. + */ + tracePtr = cmdPtr->tracePtr; while (tracePtr != NULL) { CommandTrace *nextPtr = tracePtr->nextPtr; if ((--tracePtr->refCount) <= 0) { - ckfree((char*)tracePtr); + ckfree((char *) tracePtr); } tracePtr = nextPtr; } cmdPtr->tracePtr = NULL; } - + + /* + * The list of command exported from the namespace might have changed. + * However, we do not need to recompute this just yet; next time we need + * the info will be soon enough. + */ + + TclInvalidateNsCmdLookup(cmdPtr->nsPtr); + /* - * If the command being deleted has a compile procedure, increment the - * interpreter's compileEpoch to invalidate its compiled code. This - * makes sure that we don't later try to execute old code compiled with - * command-specific (i.e., inline) bytecodes for the now-deleted - * command. This field is checked in Tcl_EvalObj and ObjInterpProc, and - * code whose compilation epoch doesn't match is recompiled. + * If the command being deleted has a compile function, increment the + * interpreter's compileEpoch to invalidate its compiled code. This makes + * sure that we don't later try to execute old code compiled with + * command-specific (i.e., inline) bytecodes for the now-deleted command. + * This field is checked in Tcl_EvalObj and ObjInterpProc, and code whose + * compilation epoch doesn't match is recompiled. */ if (cmdPtr->compileProc != NULL) { - iPtr->compileEpoch++; + iPtr->compileEpoch++; } if (cmdPtr->deleteProc != NULL) { @@ -2634,15 +2847,15 @@ Tcl_DeleteCommandFromToken(interp, cmd) * data will be a pointer to a ImportedCmdData structure describing * the "real" command that this imported command refers to. */ - + /* * If you are getting a crash during the call to deleteProc and - * cmdPtr->deleteProc is a pointer to the function free(), the - * most likely cause is that your extension allocated memory - * for the clientData argument to Tcl_CreateObjCommand() with - * the ckalloc() macro and you are now trying to deallocate - * this memory with free() instead of ckfree(). You should - * pass a pointer to your own method that calls ckfree(). + * cmdPtr->deleteProc is a pointer to the function free(), the most + * likely cause is that your extension allocated memory for the + * clientData argument to Tcl_CreateObjCommand() with the ckalloc() + * macro and you are now trying to deallocate this memory with free() + * instead of ckfree(). You should pass a pointer to your own method + * that calls ckfree(). */ (*cmdPtr->deleteProc)(cmdPtr->deleteData); @@ -2655,78 +2868,77 @@ Tcl_DeleteCommandFromToken(interp, cmd) */ for (refPtr = cmdPtr->importRefPtr; refPtr != NULL; - refPtr = nextRefPtr) { + refPtr = nextRefPtr) { nextRefPtr = refPtr->nextPtr; importCmd = (Tcl_Command) refPtr->importedCmdPtr; - Tcl_DeleteCommandFromToken(interp, importCmd); + Tcl_DeleteCommandFromToken(interp, importCmd); } /* - * Don't use hPtr to delete the hash entry here, because it's - * possible that the deletion callback renamed the command. - * Instead, use cmdPtr->hptr, and make sure that no-one else - * has already deleted the hash entry. + * Don't use hPtr to delete the hash entry here, because it's possible + * that the deletion callback renamed the command. Instead, use + * cmdPtr->hptr, and make sure that no-one else has already deleted the + * hash entry. */ if (cmdPtr->hPtr != NULL) { Tcl_DeleteHashEntry(cmdPtr->hPtr); + cmdPtr->hPtr = NULL; } /* * Mark the Command structure as no longer valid. This allows * TclExecuteByteCode to recognize when a Command has logically been * deleted and a pointer to this Command structure cached in a CmdName - * object is invalid. TclExecuteByteCode will look up the command again - * in the interpreter's command hashtable. + * object is invalid. TclExecuteByteCode will look up the command again in + * the interpreter's command hashtable. */ cmdPtr->objProc = NULL; /* - * Now free the Command structure, unless there is another reference to - * it from a CmdName Tcl object in some ByteCode code sequence. In that - * case, delay the cleanup until all references are either discarded - * (when a ByteCode is freed) or replaced by a new reference (when a - * cached CmdName Command reference is found to be invalid and - * TclExecuteByteCode looks up the command in the command hashtable). + * Now free the Command structure, unless there is another reference to it + * from a CmdName Tcl object in some ByteCode code sequence. In that case, + * delay the cleanup until all references are either discarded (when a + * ByteCode is freed) or replaced by a new reference (when a cached + * CmdName Command reference is found to be invalid and TclExecuteByteCode + * looks up the command in the command hashtable). */ - - TclCleanupCommand(cmdPtr); + + TclCleanupCommandMacro(cmdPtr); return 0; } static char * -CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags) - Interp *iPtr; /* Interpreter containing command. */ - Command *cmdPtr; /* Command whose traces are to be - * invoked. */ - CONST char *oldName; /* Command's old name, or NULL if we - * must get the name from cmdPtr */ - CONST char *newName; /* Command's new name, or NULL if - * the command is not being renamed */ - int flags; /* Flags indicating the type of traces - * to trigger, either TCL_TRACE_DELETE - * or TCL_TRACE_RENAME. */ +CallCommandTraces( + Interp *iPtr, /* Interpreter containing command. */ + Command *cmdPtr, /* Command whose traces are to be invoked. */ + const char *oldName, /* Command's old name, or NULL if we must get + * the name from cmdPtr */ + const char *newName, /* Command's new name, or NULL if the command + * is not being renamed */ + int flags) /* Flags indicating the type of traces to + * trigger, either TCL_TRACE_DELETE or + * TCL_TRACE_RENAME. */ { register CommandTrace *tracePtr; ActiveCommandTrace active; char *result; Tcl_Obj *oldNamePtr = NULL; - int mask = (TCL_TRACE_DELETE | TCL_TRACE_RENAME); /* Safety */ - - flags &= mask; + Tcl_InterpState state = NULL; if (cmdPtr->flags & CMD_TRACE_ACTIVE) { - /* - * While a rename trace is active, we will not process any more - * rename traces; while a delete trace is active we will never - * reach here -- because Tcl_DeleteCommandFromToken checks for the - * condition (cmdPtr->flags & CMD_IS_DELETED) and returns immediately - * when a command deletion is in progress. For all other traces, - * delete traces will not be invoked but a call to TraceCommandProc - * will ensure that tracePtr->clientData is freed whenever the - * command "oldName" is deleted. + /* + * While a rename trace is active, we will not process any more rename + * traces; while a delete trace is active we will never reach here - + * because Tcl_DeleteCommandFromToken checks for the condition + * (cmdPtr->flags & CMD_IS_DELETED) and returns immediately when a + * command deletion is in progress. For all other traces, delete + * traces will not be invoked but a call to TraceCommandProc will + * ensure that tracePtr->clientData is freed whenever the command + * "oldName" is deleted. */ + if (cmdPtr->flags & TCL_TRACE_RENAME) { flags &= ~TCL_TRACE_RENAME; } @@ -2736,7 +2948,7 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags) } cmdPtr->flags |= CMD_TRACE_ACTIVE; cmdPtr->refCount++; - + result = NULL; active.nextPtr = iPtr->activeCmdTracePtr; active.reverseScan = 0; @@ -2746,37 +2958,41 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags) flags |= TCL_TRACE_DESTROYED; } active.cmdPtr = cmdPtr; - - Tcl_Preserve((ClientData) iPtr); - - for (tracePtr = cmdPtr->tracePtr; tracePtr != NULL; - tracePtr = active.nextTracePtr) { - int traceFlags = (tracePtr->flags & mask); + Tcl_Preserve(iPtr); + + for (tracePtr = cmdPtr->tracePtr; tracePtr != NULL; + tracePtr = active.nextTracePtr) { active.nextTracePtr = tracePtr->nextPtr; - if (!(traceFlags & flags)) { + if (!(tracePtr->flags & flags)) { continue; } - cmdPtr->flags |= traceFlags; + cmdPtr->flags |= tracePtr->flags; if (oldName == NULL) { TclNewObj(oldNamePtr); Tcl_IncrRefCount(oldNamePtr); - Tcl_GetCommandFullName((Tcl_Interp *) iPtr, - (Tcl_Command) cmdPtr, oldNamePtr); + Tcl_GetCommandFullName((Tcl_Interp *) iPtr, + (Tcl_Command) cmdPtr, oldNamePtr); oldName = TclGetString(oldNamePtr); } tracePtr->refCount++; + if (state == NULL) { + state = Tcl_SaveInterpState((Tcl_Interp *) iPtr, TCL_OK); + } (*tracePtr->traceProc)(tracePtr->clientData, (Tcl_Interp *) iPtr, oldName, newName, flags); - cmdPtr->flags &= ~traceFlags; + cmdPtr->flags &= ~tracePtr->flags; if ((--tracePtr->refCount) <= 0) { - ckfree((char*)tracePtr); + ckfree((char *) tracePtr); } } + if (state) { + Tcl_RestoreInterpState((Tcl_Interp *) iPtr, state); + } + /* - * If a new object was created to hold the full oldName, - * free it now. + * If a new object was created to hold the full oldName, free it now. */ if (oldNamePtr != NULL) { @@ -2784,26 +3000,55 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags) } /* - * Restore the variable's flags, remove the record of our active - * traces, and then return. + * Restore the variable's flags, remove the record of our active traces, + * and then return. */ cmdPtr->flags &= ~CMD_TRACE_ACTIVE; cmdPtr->refCount--; iPtr->activeCmdTracePtr = active.nextPtr; - Tcl_Release((ClientData) iPtr); + Tcl_Release(iPtr); return result; } + +/* + *---------------------------------------------------------------------- + * + * GetCommandSource -- + * + * This function returns a Tcl_Obj with the full source string for the + * command. This insures that traces get a correct NUL-terminated command + * string. + * + *---------------------------------------------------------------------- + */ + +static Tcl_Obj * +GetCommandSource( + Interp *iPtr, + const char *command, + int numChars, + int objc, + Tcl_Obj *const objv[]) +{ + if (!command) { + return Tcl_NewListObj(objc, objv); + } + if (command == (char *) -1) { + command = TclGetSrcInfoForCmd(iPtr, &numChars); + } + return Tcl_NewStringObj(command, numChars); +} /* *---------------------------------------------------------------------- * * TclCleanupCommand -- * - * This procedure frees up a Command structure unless it is still + * This function frees up a Command structure unless it is still * referenced from an interpreter's command hashtable or from a CmdName * Tcl object representing the name of a command in a ByteCode - * instruction sequence. + * instruction sequence. * * Results: * None. @@ -2817,8 +3062,8 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags) */ void -TclCleanupCommand(cmdPtr) - register Command *cmdPtr; /* Points to the Command structure to +TclCleanupCommand( + register Command *cmdPtr) /* Points to the Command structure to * be freed. */ { cmdPtr->refCount--; @@ -2832,18 +3077,17 @@ TclCleanupCommand(cmdPtr) * * Tcl_CreateMathFunc -- * - * Creates a new math function for expressions in a given - * interpreter. + * Creates a new math function for expressions in a given interpreter. * * Results: * None. * * Side effects: - * The function defined by "name" is created or redefined. If the - * function already exists then its definition is replaced; this - * includes the builtin functions. Redefining a builtin function forces - * all existing code to be invalidated since that code may be compiled - * using an instruction specific to the replaced function. In addition, + * The Tcl function defined by "name" is created or redefined. If the + * function already exists then its definition is replaced; this includes + * the builtin functions. Redefining a builtin function forces all + * existing code to be invalidated since that code may be compiled using + * an instruction specific to the replaced function. In addition, * redefioning a non-builtin function will force existing code to be * invalidated if the number of arguments has changed. * @@ -2851,65 +3095,205 @@ TclCleanupCommand(cmdPtr) */ void -Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData) - Tcl_Interp *interp; /* Interpreter in which function is - * to be available. */ - CONST char *name; /* Name of function (e.g. "sin"). */ - int numArgs; /* Nnumber of arguments required by - * function. */ - Tcl_ValueType *argTypes; /* Array of types acceptable for - * each argument. */ - Tcl_MathProc *proc; /* Procedure that implements the - * math function. */ - ClientData clientData; /* Additional value to pass to the - * function. */ +Tcl_CreateMathFunc( + Tcl_Interp *interp, /* Interpreter in which function is to be + * available. */ + const char *name, /* Name of function (e.g. "sin"). */ + int numArgs, /* Nnumber of arguments required by + * function. */ + Tcl_ValueType *argTypes, /* Array of types acceptable for each + * argument. */ + Tcl_MathProc *proc, /* C function that implements the math + * function. */ + ClientData clientData) /* Additional value to pass to the + * function. */ { - Interp *iPtr = (Interp *) interp; - Tcl_HashEntry *hPtr; - MathFunc *mathFuncPtr; - int new, i; + Tcl_DString bigName; + OldMathFuncData *data = (OldMathFuncData *) + ckalloc(sizeof(OldMathFuncData)); + + data->proc = proc; + data->numArgs = numArgs; + data->argTypes = (Tcl_ValueType *) + ckalloc(numArgs * sizeof(Tcl_ValueType)); + memcpy(data->argTypes, argTypes, numArgs * sizeof(Tcl_ValueType)); + data->clientData = clientData; + + Tcl_DStringInit(&bigName); + Tcl_DStringAppend(&bigName, "::tcl::mathfunc::", -1); + Tcl_DStringAppend(&bigName, name, -1); + + Tcl_CreateObjCommand(interp, Tcl_DStringValue(&bigName), + OldMathFuncProc, data, OldMathFuncDeleteProc); + Tcl_DStringFree(&bigName); +} + +/* + *---------------------------------------------------------------------- + * + * OldMathFuncProc -- + * + * Dispatch to a math function created with Tcl_CreateMathFunc + * + * Results: + * Returns a standard Tcl result. + * + * Side effects: + * Whatever the math function does. + * + *---------------------------------------------------------------------- + */ - hPtr = Tcl_CreateHashEntry(&iPtr->mathFuncTable, name, &new); - if (new) { - Tcl_SetHashValue(hPtr, ckalloc(sizeof(MathFunc))); +static int +OldMathFuncProc( + ClientData clientData, /* Ponter to OldMathFuncData describing the + * function being called */ + Tcl_Interp *interp, /* Tcl interpreter */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Parameter vector */ +{ + Tcl_Obj *valuePtr; + OldMathFuncData *dataPtr = clientData; + Tcl_Value funcResult, *args; + int result; + int j, k; + double d; + + /* + * Check argument count. + */ + + if (objc != dataPtr->numArgs + 1) { + MathFuncWrongNumArgs(interp, dataPtr->numArgs+1, objc, objv); + return TCL_ERROR; } - mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); - if (!new) { - if (mathFuncPtr->builtinFuncIndex >= 0) { - /* - * We are redefining a builtin math function. Invalidate the - * interpreter's existing code by incrementing its - * compileEpoch member. This field is checked in Tcl_EvalObj - * and ObjInterpProc, and code whose compilation epoch doesn't - * match is recompiled. Newly compiled code will no longer - * treat the function as builtin. - */ + /* + * Convert arguments from Tcl_Obj's to Tcl_Value's. + */ - iPtr->compileEpoch++; - } else { + args = (Tcl_Value *) ckalloc(dataPtr->numArgs * sizeof(Tcl_Value)); + for (j = 1, k = 0; j < objc; ++j, ++k) { + + /* TODO: Convert to TclGetNumberFromObj() ? */ + valuePtr = objv[j]; + result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d); +#ifdef ACCEPT_NAN + if ((result != TCL_OK) && (valuePtr->typePtr == &tclDoubleType)) { + d = valuePtr->internalRep.doubleValue; + result = TCL_OK; + } +#endif + if (result != TCL_OK) { /* - * A non-builtin function is being redefined. We must invalidate - * existing code if the number of arguments has changed. This - * is because existing code was compiled assuming that number. + * We have a non-numeric argument. */ - if (numArgs != mathFuncPtr->numArgs) { - iPtr->compileEpoch++; + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "argument to math function didn't have numeric value",-1)); + TclCheckBadOctal(interp, Tcl_GetString(valuePtr)); + ckfree((char *)args); + return TCL_ERROR; + } + + /* + * Copy the object's numeric value to the argument record, converting + * it if necessary. + * + * NOTE: no bignum support; use the new mathfunc interface for that. + */ + + args[k].type = dataPtr->argTypes[k]; + switch (args[k].type) { + case TCL_EITHER: + if (Tcl_GetLongFromObj(NULL, valuePtr, &(args[k].intValue)) + == TCL_OK) { + args[k].type = TCL_INT; + break; + } + if (Tcl_GetWideIntFromObj(interp, valuePtr, &(args[k].wideValue)) + == TCL_OK) { + args[k].type = TCL_WIDE_INT; + break; } + args[k].type = TCL_DOUBLE; + /* FALLTHROUGH */ + + case TCL_DOUBLE: + args[k].doubleValue = d; + break; + case TCL_INT: + if (ExprIntFunc(NULL, interp, 2, &(objv[j-1])) != TCL_OK) { + ckfree((char *)args); + return TCL_ERROR; + } + valuePtr = Tcl_GetObjResult(interp); + Tcl_GetLongFromObj(NULL, valuePtr, &(args[k].intValue)); + Tcl_ResetResult(interp); + break; + case TCL_WIDE_INT: + if (ExprWideFunc(NULL, interp, 2, &(objv[j-1])) != TCL_OK) { + ckfree((char *)args); + return TCL_ERROR; + } + valuePtr = Tcl_GetObjResult(interp); + Tcl_GetWideIntFromObj(NULL, valuePtr, &(args[k].wideValue)); + Tcl_ResetResult(interp); + break; } } - - mathFuncPtr->builtinFuncIndex = -1; /* can't be a builtin function */ - if (numArgs > MAX_MATH_ARGS) { - numArgs = MAX_MATH_ARGS; + + /* + * Call the function. + */ + + errno = 0; + result = (*dataPtr->proc)(dataPtr->clientData, interp, args, &funcResult); + ckfree((char *)args); + if (result != TCL_OK) { + return result; } - mathFuncPtr->numArgs = numArgs; - for (i = 0; i < numArgs; i++) { - mathFuncPtr->argTypes[i] = argTypes[i]; + + /* + * Return the result of the call. + */ + + if (funcResult.type == TCL_INT) { + TclNewLongObj(valuePtr, funcResult.intValue); + } else if (funcResult.type == TCL_WIDE_INT) { + valuePtr = Tcl_NewWideIntObj(funcResult.wideValue); + } else { + return CheckDoubleResult(interp, funcResult.doubleValue); } - mathFuncPtr->proc = proc; - mathFuncPtr->clientData = clientData; + Tcl_SetObjResult(interp, valuePtr); + return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * OldMathFuncDeleteProc -- + * + * Cleans up after deleting a math function registered with + * Tcl_CreateMathFunc + * + * Results: + * None. + * + * Side effects: + * Frees allocated memory. + * + *---------------------------------------------------------------------- + */ + +static void +OldMathFuncDeleteProc( + ClientData clientData) +{ + OldMathFuncData *dataPtr = clientData; + + ckfree((void *) dataPtr->argTypes); + ckfree((void *) dataPtr); } /* @@ -2921,64 +3305,80 @@ Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData) * interpreter. * * Results: - * TCL_OK if it succeeds, TCL_ERROR else (leaving an error message - * in the interpreter result if that happens.) + * TCL_OK if it succeeds, TCL_ERROR else (leaving an error message in the + * interpreter result if that happens.) * * Side effects: - * If this function succeeds, the variables pointed to by the - * numArgsPtr and argTypePtr arguments will be updated to detail the - * arguments allowed by the function. The variable pointed to by the - * procPtr argument will be set to NULL if the function is a builtin - * function, and will be set to the address of the C function used to - * implement the math function otherwise (in which case the variable - * pointed to by the clientDataPtr argument will also be updated.) + * If this function succeeds, the variables pointed to by the numArgsPtr + * and argTypePtr arguments will be updated to detail the arguments + * allowed by the function. The variable pointed to by the procPtr + * argument will be set to NULL if the function is a builtin function, + * and will be set to the address of the C function used to implement the + * math function otherwise (in which case the variable pointed to by the + * clientDataPtr argument will also be updated.) * *---------------------------------------------------------------------- */ int -Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr, - clientDataPtr) - Tcl_Interp *interp; - CONST char *name; - int *numArgsPtr; - Tcl_ValueType **argTypesPtr; - Tcl_MathProc **procPtr; - ClientData *clientDataPtr; +Tcl_GetMathFuncInfo( + Tcl_Interp *interp, + const char *name, + int *numArgsPtr, + Tcl_ValueType **argTypesPtr, + Tcl_MathProc **procPtr, + ClientData *clientDataPtr) { - Interp *iPtr = (Interp *) interp; - Tcl_HashEntry *hPtr; - MathFunc *mathFuncPtr; - Tcl_ValueType *argTypes; - int i,numArgs; + Tcl_Obj *cmdNameObj; + Command *cmdPtr; - hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, name); - if (hPtr == NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "math function \"", name, "\" not known in this interpreter", - (char *) NULL); + /* + * Get the command that implements the math function. + */ + + TclNewLiteralStringObj(cmdNameObj, "tcl::mathfunc::"); + Tcl_AppendToObj(cmdNameObj, name, -1); + Tcl_IncrRefCount(cmdNameObj); + cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, cmdNameObj); + Tcl_DecrRefCount(cmdNameObj); + + /* + * Report unknown functions. + */ + + if (cmdPtr == NULL) { + Tcl_Obj *message; + + TclNewLiteralStringObj(message, "unknown math function \""); + Tcl_AppendToObj(message, name, -1); + Tcl_AppendToObj(message, "\"", 1); + Tcl_SetObjResult(interp, message); + *numArgsPtr = -1; + *argTypesPtr = NULL; + *procPtr = NULL; + *clientDataPtr = NULL; return TCL_ERROR; } - mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); - *numArgsPtr = numArgs = mathFuncPtr->numArgs; - if (numArgs == 0) { - /* Avoid doing zero-sized allocs... */ - numArgs = 1; - } - *argTypesPtr = argTypes = - (Tcl_ValueType *)ckalloc(numArgs * sizeof(Tcl_ValueType)); - for (i = 0; i < mathFuncPtr->numArgs; i++) { - argTypes[i] = mathFuncPtr->argTypes[i]; - } + /* + * Retrieve function info for user defined functions; return dummy + * information for builtins. + */ + + if (cmdPtr->objProc == &OldMathFuncProc) { + OldMathFuncData *dataPtr = cmdPtr->clientData; - if (mathFuncPtr->builtinFuncIndex == -1) { - *procPtr = (Tcl_MathProc *) NULL; + *procPtr = dataPtr->proc; + *numArgsPtr = dataPtr->numArgs; + *argTypesPtr = dataPtr->argTypes; + *clientDataPtr = dataPtr->clientData; } else { - *procPtr = mathFuncPtr->proc; - *clientDataPtr = mathFuncPtr->clientData; + *procPtr = NULL; + *numArgsPtr = -1; + *argTypesPtr = NULL; + *procPtr = NULL; + *clientDataPtr = NULL; } - return TCL_OK; } @@ -2991,9 +3391,9 @@ Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr, * interpreter. * * Results: - * A pointer to a Tcl_Obj structure with a reference count of zero, - * or NULL in the case of an error (in which case a suitable error - * message will be left in the interpreter result.) + * A pointer to a Tcl_Obj structure with a reference count of zero, or + * NULL in the case of an error (in which case a suitable error message + * will be left in the interpreter result.) * * Side effects: * None. @@ -3002,28 +3402,33 @@ Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr, */ Tcl_Obj * -Tcl_ListMathFuncs(interp, pattern) - Tcl_Interp *interp; - CONST char *pattern; +Tcl_ListMathFuncs( + Tcl_Interp *interp, + const char *pattern) { - Interp *iPtr = (Interp *) interp; - Tcl_Obj *resultList = Tcl_NewObj(); - register Tcl_HashEntry *hPtr; - Tcl_HashSearch hSearch; - CONST char *name; - - for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &hSearch); - hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - name = Tcl_GetHashKey(&iPtr->mathFuncTable, hPtr); - if ((pattern == NULL || Tcl_StringMatch(name, pattern)) && - /* I don't expect this to fail, but... */ - Tcl_ListObjAppendElement(interp, resultList, - Tcl_NewStringObj(name,-1)) != TCL_OK) { - Tcl_DecrRefCount(resultList); - return NULL; - } + Tcl_Obj *script = Tcl_NewStringObj("::info functions ", -1); + Tcl_Obj *result; + Tcl_InterpState state; + + if (pattern) { + Tcl_Obj *patternObj = Tcl_NewStringObj(pattern, -1); + Tcl_Obj *arg = Tcl_NewListObj(1, &patternObj); + + Tcl_AppendObjToObj(script, arg); + Tcl_DecrRefCount(arg); /* Should tear down patternObj too */ + } + + state = Tcl_SaveInterpState(interp, TCL_OK); + Tcl_IncrRefCount(script); + if (TCL_OK == Tcl_EvalObjEx(interp, script, 0)) { + result = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); + } else { + result = Tcl_NewObj(); } - return resultList; + Tcl_DecrRefCount(script); + Tcl_RestoreInterpState(interp, state); + + return result; } /* @@ -3031,13 +3436,12 @@ Tcl_ListMathFuncs(interp, pattern) * * TclInterpReady -- * - * Check if an interpreter is ready to eval commands or scripts, - * i.e., if it was not deleted and if the nesting level is not - * too high. + * Check if an interpreter is ready to eval commands or scripts, i.e., if + * it was not deleted and if the nesting level is not too high. * * Results: - * The return value is TCL_OK if it the interpreter is ready, - * TCL_ERROR otherwise. + * The return value is TCL_OK if it the interpreter is ready, TCL_ERROR + * otherwise. * * Side effects: * The interpreters object and string results are cleared. @@ -3045,15 +3449,18 @@ Tcl_ListMathFuncs(interp, pattern) *---------------------------------------------------------------------- */ -int -TclInterpReady(interp) - Tcl_Interp *interp; +int +TclInterpReady( + Tcl_Interp *interp) { +#if !defined(TCL_NO_STACK_CHECK) + int localInt; /* used for checking the stack */ +#endif register Interp *iPtr = (Interp *) interp; /* - * Reset both the interpreter's string and object results and clear - * out any previous error information. + * Reset both the interpreter's string and object results and clear out + * any previous error information. */ Tcl_ResetResult(interp); @@ -3061,46 +3468,53 @@ TclInterpReady(interp) /* * If the interpreter has been deleted, return an error. */ - + if (iPtr->flags & DELETED) { Tcl_ResetResult(interp); - Tcl_AppendToObj(Tcl_GetObjResult(interp), - "attempt to call eval in deleted interpreter", -1); - Tcl_SetErrorCode(interp, "CORE", "IDELETE", - "attempt to call eval in deleted interpreter", - (char *) NULL); + Tcl_AppendResult(interp, + "attempt to call eval in deleted interpreter", NULL); + Tcl_SetErrorCode(interp, "TCL", "IDELETE", + "attempt to call eval in deleted interpreter", NULL); return TCL_ERROR; } /* - * Check depth of nested calls to Tcl_Eval: if this gets too large, - * it's probably because of an infinite loop somewhere. + * Check depth of nested calls to Tcl_Eval: if this gets too large, it's + * probably because of an infinite loop somewhere. */ - if (((iPtr->numLevels) > iPtr->maxNestingDepth) - || (TclpCheckStackSpace() == 0)) { - Tcl_AppendToObj(Tcl_GetObjResult(interp), - "too many nested evaluations (infinite loop?)", -1); - return TCL_ERROR; + if (((iPtr->numLevels) <= iPtr->maxNestingDepth) + && CheckCStack(iPtr, &localInt)) { + return TCL_OK; } - return TCL_OK; + if (!CheckCStack(iPtr, &localInt)) { + Tcl_AppendResult(interp, + "out of stack space (infinite loop?)", NULL); + } else { + Tcl_AppendResult(interp, + "too many nested evaluations (infinite loop?)", NULL); + } + return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * TclEvalObjvInternal -- + * TclEvalObjvInternal + * + * This function evaluates a Tcl command that has already been parsed + * into words, with one Tcl_Obj holding each word. The caller is + * responsible for managing the iPtr->numLevels. * - * This procedure evaluates a Tcl command that has already been - * parsed into words, with one Tcl_Obj holding each word. The caller - * is responsible for managing the iPtr->numLevels. + * TclEvalObjvInternal is the backend for Tcl_EvalObjv, the bytecode + * engine also calls it directly. * * Results: - * The return value is a standard Tcl completion code such as - * TCL_OK or TCL_ERROR. A result or error message is left in - * interp's result. If an error occurs, this procedure does - * NOT add any information to the errorInfo variable. + * The return value is a standard Tcl completion code such as TCL_OK or + * TCL_ERROR. A result or error message is left in interp's result. If an + * error occurs, this function does NOT add any information to the + * errorInfo variable. * * Side effects: * Depends on the command. @@ -3109,39 +3523,39 @@ TclInterpReady(interp) */ int -TclEvalObjvInternal(interp, objc, objv, command, length, flags) - Tcl_Interp *interp; /* Interpreter in which to evaluate the - * command. Also used for error - * reporting. */ - int objc; /* Number of words in command. */ - Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are +TclEvalObjvInternal( + Tcl_Interp *interp, /* Interpreter in which to evaluate the + * command. Also used for error reporting. */ + int objc, /* Number of words in command. */ + Tcl_Obj *const objv[], /* An array of pointers to objects that are * the words that make up the command. */ - CONST char *command; /* Points to the beginning of the string - * representation of the command; this - * is used for traces. If the string - * representation of the command is - * unknown, an empty string should be - * supplied. If it is NULL, no traces will - * be called. */ - int length; /* Number of bytes in command; if -1, all + const char *command, /* Points to the beginning of the string + * representation of the command; this is used + * for traces. NULL if the string + * representation of the command is unknown is + * to be generated from (objc,objv), -1 if it + * is to be generated from bytecode + * source. This is only needed the traces. */ + int length, /* Number of bytes in command; if -1, all * characters up to the first null byte are * used. */ - int flags; /* Collection of OR-ed bits that control - * the evaluation of the script. Only + int flags) /* Collection of OR-ed bits that control the + * evaluation of the script. Only * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are * currently supported. */ - { Command *cmdPtr; Interp *iPtr = (Interp *) interp; Tcl_Obj **newObjv; int i; - CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr - * in case TCL_EVAL_GLOBAL was set. */ + CallFrame *savedVarFramePtr = NULL; + CallFrame *varFramePtr = iPtr->varFramePtr; int code = TCL_OK; int traceCode = TCL_OK; - int checkTraces = 1; + int checkTraces = 1, traced; Namespace *savedNsPtr = NULL; + Namespace *lookupNsPtr = iPtr->lookupNsPtr; + Tcl_Obj *commandPtr = NULL; if (TclInterpReady(interp) == TCL_ERROR) { return TCL_ERROR; @@ -3151,93 +3565,108 @@ TclEvalObjvInternal(interp, objc, objv, command, length, flags) return TCL_OK; } + /* + * If any execution traces rename or delete the current command, we may + * need (at most) two passes here. + */ + + reparseBecauseOfTraces: /* - * If any execution traces rename or delete the current command, - * we may need (at most) two passes here. + * Configure evaluation context to match the requested flags. */ - savedVarFramePtr = iPtr->varFramePtr; - while (1) { - - /* Configure evaluation context to match the requested flags */ - if (flags & TCL_EVAL_GLOBAL) { - iPtr->varFramePtr = NULL; - } else if ((flags & TCL_EVAL_INVOKE) && iPtr->varFramePtr) { - savedNsPtr = iPtr->varFramePtr->nsPtr; - iPtr->varFramePtr->nsPtr = iPtr->globalNsPtr; - } - - /* - * Find the procedure to execute this command. If there isn't one, - * then see if there is a command "unknown". If so, create a new - * word array with "unknown" as the first word and the original - * command words as arguments. Then call ourselves recursively - * to execute it. - */ - cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]); - if (cmdPtr == NULL) { - newObjv = (Tcl_Obj **) ckalloc((unsigned) - ((objc + 1) * sizeof (Tcl_Obj *))); - for (i = objc-1; i >= 0; i--) { - newObjv[i+1] = objv[i]; - } - newObjv[0] = Tcl_NewStringObj("::unknown", -1); - Tcl_IncrRefCount(newObjv[0]); - cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]); - if (cmdPtr == NULL) { - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "invalid command name \"", Tcl_GetString(objv[0]), "\"", - (char *) NULL); - code = TCL_ERROR; + if (flags) { + if (flags & TCL_EVAL_INVOKE) { + savedNsPtr = varFramePtr->nsPtr; + if (lookupNsPtr) { + varFramePtr->nsPtr = lookupNsPtr; + iPtr->lookupNsPtr = NULL; } else { - iPtr->numLevels++; - code = TclEvalObjvInternal(interp, objc+1, newObjv, - command, length, 0); - iPtr->numLevels--; + varFramePtr->nsPtr = iPtr->globalNsPtr; } - Tcl_DecrRefCount(newObjv[0]); - ckfree((char *) newObjv); - if (savedNsPtr) { - iPtr->varFramePtr->nsPtr = savedNsPtr; + } else if ((flags & TCL_EVAL_GLOBAL) + && (varFramePtr != iPtr->rootFramePtr) && !savedVarFramePtr) { + varFramePtr = iPtr->rootFramePtr; + savedVarFramePtr = iPtr->varFramePtr; + iPtr->varFramePtr = varFramePtr; + } + } + + /* + * Find the function to execute this command. If there isn't one, then see + * if there is an unknown command handler registered for this namespace. + * If so, create a new word array with the handler as the first words and + * the original command words as arguments. Then call ourselves + * recursively to execute it. + */ + + cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]); + if (!cmdPtr) { + goto notFound; + } + + if (savedNsPtr) { + varFramePtr->nsPtr = savedNsPtr; + } else if (iPtr->ensembleRewrite.sourceObjs) { + /* + * TCL_EVAL_INVOKE was not set: clear rewrite rules + */ + + iPtr->ensembleRewrite.sourceObjs = NULL; + } + + /* + * Call trace functions if needed. + */ + + traced = (iPtr->tracePtr || (cmdPtr->flags & CMD_HAS_EXEC_TRACES)); + if (traced && checkTraces) { + int cmdEpoch = cmdPtr->cmdEpoch; + int newEpoch; + + /* + * Insure that we have a correct nul-terminated command string for the + * trace code. + */ + + commandPtr = GetCommandSource(iPtr, command, length, objc, objv); + command = TclGetStringFromObj(commandPtr, &length); + + /* + * Execute any command or execution traces. Note that we bump up the + * command's reference count for the duration of the calling of the + * traces so that the structure doesn't go away underneath our feet. + */ + + cmdPtr->refCount++; + if (iPtr->tracePtr && (traceCode == TCL_OK)) { + traceCode = TclCheckInterpTraces(interp, command, length, + cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); + } + if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) { + traceCode = TclCheckExecutionTraces(interp, command, length, + cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); + } + newEpoch = cmdPtr->cmdEpoch; + TclCleanupCommandMacro(cmdPtr); + + /* + * If the traces modified/deleted the command or any existing traces, + * they will update the command's epoch. When that happens, set + * checkTraces is set to 0 to prevent the re-calling of traces (and + * any possible infinite loop) and we go back to re-find the command + * implementation. + */ + + if (cmdEpoch != newEpoch) { + checkTraces = 0; + if (commandPtr) { + Tcl_DecrRefCount(commandPtr); + commandPtr = NULL; } - goto done; - } - if (savedNsPtr) { - iPtr->varFramePtr->nsPtr = savedNsPtr; + goto reparseBecauseOfTraces; } - - /* - * Call trace procedures if needed. - */ - if ((checkTraces) && (command != NULL)) { - int cmdEpoch = cmdPtr->cmdEpoch; - int newEpoch; - - cmdPtr->refCount++; - /* - * If the first set of traces modifies/deletes the command or - * any existing traces, then the set checkTraces to 0 and - * go through this while loop one more time. - */ - if (iPtr->tracePtr != NULL && traceCode == TCL_OK) { - traceCode = TclCheckInterpTraces(interp, command, length, - cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); - } - if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) - && (traceCode == TCL_OK)) { - traceCode = TclCheckExecutionTraces(interp, command, length, - cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); - } - newEpoch = cmdPtr->cmdEpoch; - TclCleanupCommand(cmdPtr); - if (cmdEpoch != newEpoch) { - /* The command has been modified in some way */ - checkTraces = 0; - continue; - } - } - break; } #ifdef USE_DTRACE @@ -3251,14 +3680,24 @@ TclEvalObjvInternal(interp, objc, objv, command, length, flags) TCL_DTRACE_CMD_ARGS(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], a[9]); } + if (TCL_DTRACE_CMD_INFO_ENABLED() && iPtr->cmdFramePtr) { + Tcl_Obj *info = TclInfoFrame(interp, iPtr->cmdFramePtr); + char *a[4]; int i[2]; + + TclDTraceInfo(info, a, i); + TCL_DTRACE_CMD_INFO(a[0], a[1], a[2], a[3], i[0], i[1]); + TclDecrRefCount(info); + } #endif /* USE_DTRACE */ /* * Finally, invoke the command's Tcl_ObjCmdProc. */ + cmdPtr->refCount++; iPtr->cmdCount++; - if ( code == TCL_OK && traceCode == TCL_OK) { + if (code == TCL_OK && traceCode == TCL_OK + && !TclLimitExceeded(iPtr->limit)) { if (TCL_DTRACE_CMD_ENTRY_ENABLED()) { TCL_DTRACE_CMD_ENTRY(TclGetString(objv[0]), objc - 1, (Tcl_Obj **)(objv + 1)); @@ -3268,48 +3707,58 @@ TclEvalObjvInternal(interp, objc, objv, command, length, flags) TCL_DTRACE_CMD_RETURN(TclGetString(objv[0]), code); } } - if (Tcl_AsyncReady()) { + + if (TclAsyncReady(iPtr)) { code = Tcl_AsyncInvoke(interp, code); } + if (code == TCL_OK && TclLimitReady(iPtr->limit)) { + code = Tcl_LimitCheck(interp); + } /* * Call 'leave' command traces */ - if (!(cmdPtr->flags & CMD_IS_DELETED)) { - int saveErrFlags = iPtr->flags - & (ERR_IN_PROGRESS | ERR_ALREADY_LOGGED | ERROR_CODE_SET); - if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) { - traceCode = TclCheckExecutionTraces (interp, command, length, - cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv); - } - if (iPtr->tracePtr != NULL && traceCode == TCL_OK) { - traceCode = TclCheckInterpTraces(interp, command, length, - cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv); - } - if (traceCode == TCL_OK) { - iPtr->flags |= saveErrFlags; + + if (traced) { + if (!(cmdPtr->flags & CMD_IS_DELETED)) { + if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && traceCode == TCL_OK){ + traceCode = TclCheckExecutionTraces(interp, command, length, + cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv); + } + if (iPtr->tracePtr != NULL && traceCode == TCL_OK) { + traceCode = TclCheckInterpTraces(interp, command, length, + cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv); + } + } + + /* + * If one of the trace invocation resulted in error, then change the + * result code accordingly. Note, that the interp->result should + * already be set correctly by the call to TraceExecutionProc. + */ + + if (traceCode != TCL_OK) { + code = traceCode; + } + if (commandPtr) { + Tcl_DecrRefCount(commandPtr); } } - TclCleanupCommand(cmdPtr); /* - * If one of the trace invocation resulted in error, then - * change the result code accordingly. Note, that the - * interp->result should already be set correctly by the - * call to TraceExecutionProc. + * Decrement the reference count of cmdPtr and deallocate it if it has + * dropped to zero. */ - if (traceCode != TCL_OK) { - code = traceCode; - } - + TclCleanupCommandMacro(cmdPtr); + /* - * If the interpreter has a non-empty string result, the result - * object is either empty or stale because some procedure set - * interp->result directly. If so, move the string result to the - * result object, then reset the string result. + * If the interpreter has a non-empty string result, the result object is + * either empty or stale because some function set interp->result + * directly. If so, move the string result to the result object, then + * reset the string result. */ - + if (*(iPtr->result) != 0) { (void) Tcl_GetObjResult(interp); } @@ -3319,532 +3768,213 @@ TclEvalObjvInternal(interp, objc, objv, command, length, flags) Tcl_Obj *r; r = Tcl_GetObjResult(interp); - TCL_DTRACE_CMD_RESULT(TclGetString(objv[0]), code, TclGetString(r), r); + TCL_DTRACE_CMD_RESULT(TclGetString(objv[0]), code, TclGetString(r),r); } #endif /* USE_DTRACE */ - done: - iPtr->varFramePtr = savedVarFramePtr; + done: + if (savedVarFramePtr) { + iPtr->varFramePtr = savedVarFramePtr; + } return code; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_EvalObjv -- - * - * This procedure evaluates a Tcl command that has already been - * parsed into words, with one Tcl_Obj holding each word. - * - * Results: - * The return value is a standard Tcl completion code such as - * TCL_OK or TCL_ERROR. A result or error message is left in - * interp's result. - * - * Side effects: - * Depends on the command. - * - *---------------------------------------------------------------------- - */ -int -Tcl_EvalObjv(interp, objc, objv, flags) - Tcl_Interp *interp; /* Interpreter in which to evaluate the - * command. Also used for error - * reporting. */ - int objc; /* Number of words in command. */ - Tcl_Obj *CONST objv[]; /* An array of pointers to objects that are - * the words that make up the command. */ - int flags; /* Collection of OR-ed bits that control - * the evaluation of the script. Only - * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE - * are currently supported. */ -{ - Interp *iPtr = (Interp *)interp; - Trace *tracePtr; - Tcl_DString cmdBuf; - char *cmdString = ""; /* A command string is only necessary for - * command traces or error logs; it will be - * generated to replace this default value if - * necessary. */ - int cmdLen = 0; /* a non-zero value indicates that a command - * string was generated. */ - int code = TCL_OK; - int i; - int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); - - for (tracePtr = iPtr->tracePtr; tracePtr; tracePtr = tracePtr->nextPtr) { - if ((tracePtr->level == 0) || (iPtr->numLevels <= tracePtr->level)) { - /* - * The command may be needed for an execution trace. Generate a - * command string. - */ - - Tcl_DStringInit(&cmdBuf); - for (i = 0; i < objc; i++) { - Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i])); + notFound: + { + Namespace *currNsPtr = NULL; /* Used to check for and invoke any + * registered unknown command handler + * for the current namespace (TIP + * 181). */ + int newObjc, handlerObjc; + Tcl_Obj **handlerObjv; + + currNsPtr = varFramePtr->nsPtr; + if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) { + currNsPtr = iPtr->globalNsPtr; + if (currNsPtr == NULL) { + Tcl_Panic("TclEvalObjvInternal: NULL global namespace pointer"); } - cmdString = Tcl_DStringValue(&cmdBuf); - cmdLen = Tcl_DStringLength(&cmdBuf); - break; } - } - iPtr->numLevels++; - code = TclEvalObjvInternal(interp, objc, objv, cmdString, cmdLen, flags); - iPtr->numLevels--; + /* + * Check to see if the resolution namespace has lost its unknown + * handler. If so, reset it to "::unknown". + */ - /* - * If we are again at the top level, process any unusual - * return code returned by the evaluated code. - */ - - if (iPtr->numLevels == 0) { - if (code == TCL_RETURN) { - code = TclUpdateReturnInfo(iPtr); + if (currNsPtr->unknownHandlerPtr == NULL) { + TclNewLiteralStringObj(currNsPtr->unknownHandlerPtr, "::unknown"); + Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr); } - if ((code != TCL_OK) && (code != TCL_ERROR) - && !allowExceptions) { - ProcessUnexpectedResult(interp, code); - code = TCL_ERROR; + + /* + * Get the list of words for the unknown handler and allocate enough + * space to hold both the handler prefix and all words of the command + * invokation itself. + */ + + Tcl_ListObjGetElements(NULL, currNsPtr->unknownHandlerPtr, + &handlerObjc, &handlerObjv); + newObjc = objc + handlerObjc; + newObjv = (Tcl_Obj **) TclStackAlloc(interp, + (int) sizeof(Tcl_Obj *) * newObjc); + + /* + * Copy command prefix from unknown handler and add on the real + * command's full argument list. Note that we only use memcpy() once + * because we have to increment the reference count of all the handler + * arguments anyway. + */ + + for (i = 0; i < handlerObjc; ++i) { + newObjv[i] = handlerObjv[i]; + Tcl_IncrRefCount(newObjv[i]); } - } - - if ((code == TCL_ERROR) && !(flags & TCL_EVAL_INVOKE)) { + memcpy(newObjv+handlerObjc, objv, sizeof(Tcl_Obj *) * (unsigned)objc); - /* - * If there was an error, a command string will be needed for the - * error log: generate it now if it was not done previously. + /* + * Look up and invoke the handler (by recursive call to this + * function). If there is no handler at all, instead of doing the + * recursive call we just generate a generic error message; it would + * be an infinite-recursion nightmare otherwise. */ - if (cmdLen == 0) { - Tcl_DStringInit(&cmdBuf); - for (i = 0; i < objc; i++) { - Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i])); - } - cmdString = Tcl_DStringValue(&cmdBuf); - cmdLen = Tcl_DStringLength(&cmdBuf); + cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]); + if (cmdPtr == NULL) { + Tcl_AppendResult(interp, "invalid command name \"", + TclGetString(objv[0]), "\"", NULL); + code = TCL_ERROR; + } else { + iPtr->numLevels++; + code = TclEvalObjvInternal(interp, newObjc, newObjv, command, + length, 0); + iPtr->numLevels--; } - Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen); - } - if (cmdLen != 0) { - Tcl_DStringFree(&cmdBuf); + /* + * Release any resources we locked and allocated during the handler + * call. + */ + + for (i = 0; i < handlerObjc; ++i) { + Tcl_DecrRefCount(newObjv[i]); + } + TclStackFree(interp, newObjv); + if (savedNsPtr) { + varFramePtr->nsPtr = savedNsPtr; + } + goto done; } - return code; } /* *---------------------------------------------------------------------- * - * Tcl_LogCommandInfo -- + * Tcl_EvalObjv -- * - * This procedure is invoked after an error occurs in an interpreter. - * It adds information to the "errorInfo" variable to describe the - * command that was being executed when the error occurred. + * This function evaluates a Tcl command that has already been parsed + * into words, with one Tcl_Obj holding each word. * * Results: - * None. + * The return value is a standard Tcl completion code such as TCL_OK or + * TCL_ERROR. A result or error message is left in interp's result. * * Side effects: - * Information about the command is added to errorInfo and the - * line number stored internally in the interpreter is set. If this - * is the first call to this procedure or Tcl_AddObjErrorInfo since - * an error occurred, then old information in errorInfo is - * deleted. + * Depends on the command. * *---------------------------------------------------------------------- */ -void -Tcl_LogCommandInfo(interp, script, command, length) - Tcl_Interp *interp; /* Interpreter in which to log information. */ - CONST char *script; /* First character in script containing - * command (must be <= command). */ - CONST char *command; /* First character in command that - * generated the error. */ - int length; /* Number of bytes in command (-1 means - * use all bytes up to first null byte). */ +int +Tcl_EvalObjv( + Tcl_Interp *interp, /* Interpreter in which to evaluate the + * command. Also used for error reporting. */ + int objc, /* Number of words in command. */ + Tcl_Obj *const objv[], /* An array of pointers to objects that are + * the words that make up the command. */ + int flags) /* Collection of OR-ed bits that control the + * evaluation of the script. Only + * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are + * currently supported. */ { - char buffer[200]; - register CONST char *p; - char *ellipsis = ""; Interp *iPtr = (Interp *) interp; + int code = TCL_OK; + int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); + + iPtr->numLevels++; + code = TclEvalObjvInternal(interp, objc, objv, NULL, 0, flags); + iPtr->numLevels--; + + if (code == TCL_OK) { + return code; + } else { - if (iPtr->flags & ERR_ALREADY_LOGGED) { /* - * Someone else has already logged error information for this - * command; we shouldn't add anything more. + * If we are again at the top level, process any unusual return code + * returned by the evaluated code. */ - return; - } + if (iPtr->numLevels == 0) { + if (code == TCL_RETURN) { + code = TclUpdateReturnInfo(iPtr); + } + if ((code != TCL_ERROR) && !allowExceptions) { + ProcessUnexpectedResult(interp, code); + code = TCL_ERROR; + } + } - /* - * Compute the line number where the error occurred. - */ + if ((code == TCL_ERROR) && !(flags & TCL_EVAL_INVOKE)) { + /* + * If there was an error, a command string will be needed for the + * error log: generate it now. Do not worry too much about doing + * it expensively. + */ - iPtr->errorLine = 1; - for (p = script; p != command; p++) { - if (*p == '\n') { - iPtr->errorLine++; - } - } + Tcl_Obj *listPtr; + char *cmdString; + int cmdLen; - /* - * Create an error message to add to errorInfo, including up to a - * maximum number of characters of the command. - */ + listPtr = Tcl_NewListObj(objc, objv); + cmdString = Tcl_GetStringFromObj(listPtr, &cmdLen); + Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen); + Tcl_DecrRefCount(listPtr); + } - if (length < 0) { - length = strlen(command); - } - if (length > 150) { - length = 150; - ellipsis = "..."; - } - while ( (command[length] & 0xC0) == 0x80 ) { - /* - * Back up truncation point so that we don't truncate in the - * middle of a multi-byte character (in UTF-8) - */ - length--; - ellipsis = "..."; - } - if (!(iPtr->flags & ERR_IN_PROGRESS)) { - sprintf(buffer, "\n while executing\n\"%.*s%s\"", - length, command, ellipsis); - } else { - sprintf(buffer, "\n invoked from within\n\"%.*s%s\"", - length, command, ellipsis); + return code; } - Tcl_AddObjErrorInfo(interp, buffer, -1); - iPtr->flags &= ~ERR_ALREADY_LOGGED; } /* *---------------------------------------------------------------------- * - * Tcl_EvalTokensStandard, EvalTokensStandard -- + * Tcl_EvalTokensStandard -- + * + * Given an array of tokens parsed from a Tcl command (e.g., the tokens + * that make up a word or the index for an array variable) this function + * evaluates the tokens and concatenates their values to form a single + * result value. * - * Given an array of tokens parsed from a Tcl command (e.g., the - * tokens that make up a word or the index for an array variable) - * this procedure evaluates the tokens and concatenates their - * values to form a single result value. - * * Results: - * The return value is a standard Tcl completion code such as - * TCL_OK or TCL_ERROR. A result or error message is left in - * interp's result. + * The return value is a standard Tcl completion code such as TCL_OK or + * TCL_ERROR. A result or error message is left in interp's result. * * Side effects: * Depends on the array of tokens being evaled. * - * TIP #280 : Keep public API, internally extended API. *---------------------------------------------------------------------- */ int -Tcl_EvalTokensStandard(interp, tokenPtr, count) - Tcl_Interp *interp; /* Interpreter in which to lookup - * variables, execute nested commands, - * and report errors. */ - Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens - * to evaluate and concatenate. */ - int count; /* Number of tokens to consider at tokenPtr. +Tcl_EvalTokensStandard( + Tcl_Interp *interp, /* Interpreter in which to lookup variables, + * execute nested commands, and report + * errors. */ + Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens to + * evaluate and concatenate. */ + int count) /* Number of tokens to consider at tokenPtr. * Must be at least 1. */ { -#ifdef TCL_TIP280 - return EvalTokensStandard (interp, tokenPtr, count, 1, NULL, NULL); -} - -static int -EvalTokensStandard(interp, tokenPtr, count, line, clNextOuter, outerScript) - Tcl_Interp *interp; /* Interpreter in which to lookup - * variables, execute nested commands, - * and report errors. */ - Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens - * to evaluate and concatenate. */ - int count; /* Number of tokens to consider at tokenPtr. - * Must be at least 1. */ - int line; /* The line the script starts on. */ - int* clNextOuter; /* Information about an outer context for */ - CONST char* outerScript; /* continuation line data. This is set by - * EvalEx() to properly handle [...]-nested - * commands. The 'outerScript' refers to the - * most-outer script containing the embedded - * command, which is refered to by 'script'. The - * 'clNextOuter' refers to the current entry in - * the table of continuation lines in this - * "master script", and the character offsets are - * relative to the 'outerScript' as well. - * - * If outerScript == script, then this call is for - * words in the outer-most script/command. See - * Tcl_EvalEx() and TclEvalObjEx() for the places - * generating arguments for which this is true. - */ -{ -#endif - Tcl_Obj *resultPtr, *indexPtr, *valuePtr; - char buffer[TCL_UTF_MAX]; -#ifdef TCL_MEM_DEBUG -# define MAX_VAR_CHARS 5 -#else -# define MAX_VAR_CHARS 30 -#endif - char nameBuffer[MAX_VAR_CHARS+1]; - char *varName, *index; - CONST char *p = NULL; /* Initialized to avoid compiler warning. */ - int length, code; -#ifdef TCL_TIP280 -#define NUM_STATIC_POS 20 - int isLiteral, maxNumCL, numCL, i, adjust; - int* clPosition = NULL; - Interp* iPtr = (Interp*) interp; - int inFile = iPtr->evalFlags & TCL_EVAL_FILE; -#endif - - /* - * The only tricky thing about this procedure is that it attempts to - * avoid object creation and string copying whenever possible. For - * example, if the value is just a nested command, then use the - * command's result object directly. - */ - - code = TCL_OK; - resultPtr = NULL; - Tcl_ResetResult(interp); -#ifdef TCL_TIP280 - /* - * For the handling of continuation lines in literals we first check if - * this is actually a literal. For if not we can forego the additional - * processing. Otherwise we pre-allocate a small table to store the - * locations of all continuation lines we find in this literal, if - * any. The table is extended if needed. - */ - - numCL = 0; - maxNumCL = 0; - isLiteral = 1; - for (i=0 ; i < count; i++) { - if ((tokenPtr[i].type != TCL_TOKEN_TEXT) && - (tokenPtr[i].type != TCL_TOKEN_BS)) { - isLiteral = 0; - break; - } - } - - if (isLiteral) { - maxNumCL = NUM_STATIC_POS; - clPosition = (int*) ckalloc (maxNumCL*sizeof(int)); - } - adjust = 0; -#endif - for ( ; count > 0; count--, tokenPtr++) { - valuePtr = NULL; - - /* - * The switch statement below computes the next value to be - * concat to the result, as either a range of text or an - * object. - */ - - switch (tokenPtr->type) { - case TCL_TOKEN_TEXT: - p = tokenPtr->start; - length = tokenPtr->size; - break; - - case TCL_TOKEN_BS: - length = TclParseBackslash(tokenPtr->start, tokenPtr->size, - (int *) NULL, buffer); - p = buffer; -#ifdef TCL_TIP280 - /* - * If the backslash sequence we found is in a literal, and - * represented a continuation line, we compute and store its - * location (as char offset to the beginning of the _result_ - * script). We may have to extend the table of locations. - * - * Note that the continuation line information is relevant - * even if the word we are processing is not a literal, as it - * can affect nested commands. See the branch for - * TCL_TOKEN_COMMAND below, where the adjustment we are - * tracking here is taken into account. The good thing is that - * we do not need a table of everything, just the number of - * lines we have to add as correction. - */ - - if ((length == 1) && (buffer[0] == ' ') && - (tokenPtr->start[1] == '\n')) { - if (isLiteral) { - int clPos; - if (resultPtr == 0) { - clPos = 0; - } else { - Tcl_GetStringFromObj(resultPtr, &clPos); - } - - if (numCL >= maxNumCL) { - maxNumCL *= 2; - clPosition = (int*) ckrealloc ((char*)clPosition, - maxNumCL*sizeof(int)); - } - clPosition[numCL] = clPos; - numCL ++; - } - adjust ++; - } -#endif - break; - - case TCL_TOKEN_COMMAND: { - Interp *iPtr = (Interp *) interp; - iPtr->numLevels++; - code = TclInterpReady(interp); - if (code == TCL_OK) { -#ifndef TCL_TIP280 - code = Tcl_EvalEx(interp, - tokenPtr->start+1, tokenPtr->size-2, 0); -#else - /* TIP #280: Transfer line information to nested command */ - TclAdvanceContinuations (&line, &clNextOuter, - tokenPtr->start - outerScript); - code = EvalEx(interp, - tokenPtr->start+1, tokenPtr->size-2, 0, - line + adjust, clNextOuter, outerScript); - - /* - * Restore flag reset by the nested eval for future - * bracketed commands and their CmdFrame setup - */ - if (inFile) { - iPtr->evalFlags |= TCL_EVAL_FILE; - } -#endif - } - iPtr->numLevels--; - if (code != TCL_OK) { - goto done; - } - valuePtr = Tcl_GetObjResult(interp); - break; - } - - case TCL_TOKEN_VARIABLE: - if (tokenPtr->numComponents == 1) { - indexPtr = NULL; - index = NULL; - } else { -#ifndef TCL_TIP280 - code = Tcl_EvalTokensStandard(interp, tokenPtr+2, - tokenPtr->numComponents - 1); -#else - /* TIP #280: Transfer line information to nested command */ - code = EvalTokensStandard(interp, tokenPtr+2, - tokenPtr->numComponents - 1, line, NULL, NULL); -#endif - if (code != TCL_OK) { - goto done; - } - indexPtr = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(indexPtr); - index = Tcl_GetString(indexPtr); - } - - /* - * We have to make a copy of the variable name in order - * to have a null-terminated string. We can't make a - * temporary modification to the script to null-terminate - * the name, because a trace callback might potentially - * reuse the script and be affected by the null character. - */ - - if (tokenPtr[1].size <= MAX_VAR_CHARS) { - varName = nameBuffer; - } else { - varName = ckalloc((unsigned) (tokenPtr[1].size + 1)); - } - strncpy(varName, tokenPtr[1].start, (size_t) tokenPtr[1].size); - varName[tokenPtr[1].size] = 0; - valuePtr = Tcl_GetVar2Ex(interp, varName, index, - TCL_LEAVE_ERR_MSG); - if (varName != nameBuffer) { - ckfree(varName); - } - if (indexPtr != NULL) { - Tcl_DecrRefCount(indexPtr); - } - if (valuePtr == NULL) { - code = TCL_ERROR; - goto done; - } - count -= tokenPtr->numComponents; - tokenPtr += tokenPtr->numComponents; - break; - - default: - panic("unexpected token type in Tcl_EvalTokensStandard"); - } - - /* - * If valuePtr isn't NULL, the next piece of text comes from that - * object; otherwise, take length bytes starting at p. - */ - - if (resultPtr == NULL) { - if (valuePtr != NULL) { - resultPtr = valuePtr; - } else { - resultPtr = Tcl_NewStringObj(p, length); - } - Tcl_IncrRefCount(resultPtr); - } else { - if (Tcl_IsShared(resultPtr)) { - Tcl_DecrRefCount(resultPtr); - resultPtr = Tcl_DuplicateObj(resultPtr); - Tcl_IncrRefCount(resultPtr); - } - if (valuePtr != NULL) { - p = Tcl_GetStringFromObj(valuePtr, &length); - } - Tcl_AppendToObj(resultPtr, p, length); - } - } - if (resultPtr != NULL) { - Tcl_SetObjResult(interp, resultPtr); -#ifdef TCL_TIP280 - /* - * If the code found continuation lines (which implies that this word - * is a literal), then we store the accumulated table of locations in - * the thread-global data structure for the bytecode compiler to find - * later, assuming that the literal is a script which will be - * compiled. - */ - - if (numCL) { - TclContinuationsEnter(resultPtr, numCL, clPosition); - } - - /* - * Release the temp table we used to collect the locations of - * continuation lines, if any. - */ - - if (maxNumCL) { - ckfree ((char*) clPosition); - } -#endif - } else { - code = TCL_ERROR; - } - - done: - if (resultPtr != NULL) { - Tcl_DecrRefCount(resultPtr); - } - return code; + return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL, 1, + NULL, NULL); } /* @@ -3852,67 +3982,62 @@ EvalTokensStandard(interp, tokenPtr, count, line, clNextOuter, outerScript) * * Tcl_EvalTokens -- * - * Given an array of tokens parsed from a Tcl command (e.g., the - * tokens that make up a word or the index for an array variable) - * this procedure evaluates the tokens and concatenates their - * values to form a single result value. + * Given an array of tokens parsed from a Tcl command (e.g., the tokens + * that make up a word or the index for an array variable) this function + * evaluates the tokens and concatenates their values to form a single + * result value. * * Results: - * The return value is a pointer to a newly allocated Tcl_Obj - * containing the value of the array of tokens. The reference - * count of the returned object has been incremented. If an error - * occurs in evaluating the tokens then a NULL value is returned - * and an error message is left in interp's result. + * The return value is a pointer to a newly allocated Tcl_Obj containing + * the value of the array of tokens. The reference count of the returned + * object has been incremented. If an error occurs in evaluating the + * tokens then a NULL value is returned and an error message is left in + * interp's result. * * Side effects: * A new object is allocated to hold the result. * *---------------------------------------------------------------------- * - * This uses a non-standard return convention; its use is now deprecated. - * It is a wrapper for the new function Tcl_EvalTokensStandard, and is not - * used in the core any longer. It is only kept for backward compatibility. + * This uses a non-standard return convention; its use is now deprecated. It + * is a wrapper for the new function Tcl_EvalTokensStandard, and is not used + * in the core any longer. It is only kept for backward compatibility. */ Tcl_Obj * -Tcl_EvalTokens(interp, tokenPtr, count) - Tcl_Interp *interp; /* Interpreter in which to lookup - * variables, execute nested commands, - * and report errors. */ - Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens - * to evaluate and concatenate. */ - int count; /* Number of tokens to consider at tokenPtr. +Tcl_EvalTokens( + Tcl_Interp *interp, /* Interpreter in which to lookup variables, + * execute nested commands, and report + * errors. */ + Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens to + * evaluate and concatenate. */ + int count) /* Number of tokens to consider at tokenPtr. * Must be at least 1. */ { - int code; Tcl_Obj *resPtr; - - code = Tcl_EvalTokensStandard(interp, tokenPtr, count); - if (code == TCL_OK) { - resPtr = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(resPtr); - Tcl_ResetResult(interp); - return resPtr; - } else { + + if (Tcl_EvalTokensStandard(interp, tokenPtr, count) != TCL_OK) { return NULL; } + resPtr = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(resPtr); + Tcl_ResetResult(interp); + return resPtr; } - /* *---------------------------------------------------------------------- * - * Tcl_EvalEx, EvalEx -- + * Tcl_EvalEx, TclEvalEx -- * - * This procedure evaluates a Tcl script without using the compiler - * or byte-code interpreter. It just parses the script, creates - * values for each word of each command, then calls EvalObjv - * to execute each command. + * This function evaluates a Tcl script without using the compiler or + * byte-code interpreter. It just parses the script, creates values for + * each word of each command, then calls EvalObjv to execute each + * command. * * Results: - * The return value is a standard Tcl completion code such as - * TCL_OK or TCL_ERROR. A result or error message is left in - * interp's result. + * The return value is a standard Tcl completion code such as TCL_OK or + * TCL_ERROR. A result or error message is left in interp's result. * * Side effects: * Depends on the script. @@ -3922,38 +4047,35 @@ Tcl_EvalTokens(interp, tokenPtr, count) */ int -Tcl_EvalEx(interp, script, numBytes, flags) - Tcl_Interp *interp; /* Interpreter in which to evaluate the - * script. Also used for error reporting. */ - CONST char *script; /* First character of script to evaluate. */ - int numBytes; /* Number of bytes in script. If < 0, the +Tcl_EvalEx( + Tcl_Interp *interp, /* Interpreter in which to evaluate the + * script. Also used for error reporting. */ + const char *script, /* First character of script to evaluate. */ + int numBytes, /* Number of bytes in script. If < 0, the * script consists of all bytes up to the * first null character. */ - int flags; /* Collection of OR-ed bits that control - * the evaluation of the script. Only - * TCL_EVAL_GLOBAL is currently - * supported. */ + int flags) /* Collection of OR-ed bits that control the + * evaluation of the script. Only + * TCL_EVAL_GLOBAL is currently supported. */ { -#ifdef TCL_TIP280 - return EvalEx (interp, script, numBytes, flags, 1, NULL, script); + return TclEvalEx(interp, script, numBytes, flags, 1, NULL, script); } -static int -EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) - Tcl_Interp *interp; /* Interpreter in which to evaluate the - * script. Also used for error reporting. */ - CONST char *script; /* First character of script to evaluate. */ - int numBytes; /* Number of bytes in script. If < 0, the +int +TclEvalEx( + Tcl_Interp *interp, /* Interpreter in which to evaluate the + * script. Also used for error reporting. */ + const char *script, /* First character of script to evaluate. */ + int numBytes, /* Number of bytes in script. If < 0, the * script consists of all bytes up to the - * first null character. */ - int flags; /* Collection of OR-ed bits that control - * the evaluation of the script. Only - * TCL_EVAL_GLOBAL is currently - * supported. */ - int line; /* The line the script starts on. */ - int* clNextOuter; /* Information about an outer context for */ - CONST char* outerScript; /* continuation line data. This is set only in - * EvalTokensStandard(), to properly handle + * first NUL character. */ + int flags, /* Collection of OR-ed bits that control the + * evaluation of the script. Only + * TCL_EVAL_GLOBAL is currently supported. */ + int line, /* The line the script starts on. */ + int* clNextOuter, /* Information about an outer context for */ + CONST char* outerScript) /* continuation line data. This is set only in + * TclSubstTokens(), to properly handle * [...]-nested commands. The 'outerScript' * refers to the most-outer script containing the * embedded command, which is refered to by @@ -3969,31 +4091,32 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) * generating arguments for which this is true. */ { -#endif Interp *iPtr = (Interp *) interp; - CONST char *p, *next; - Tcl_Parse parse; -#define NUM_STATIC_OBJS 20 - Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv; + const char *p, *next; + const unsigned int minObjs = 20; + Tcl_Obj **objv, **objvSpace; + int *expand, *lines, *lineSpace; Tcl_Token *tokenPtr; - int code = TCL_OK; - int i, commandLength, bytesLeft, nested; - CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr - * in case TCL_EVAL_GLOBAL was set. */ + int commandLength, bytesLeft, expandRequested, code = TCL_OK; + CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case + * TCL_EVAL_GLOBAL was set. */ int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); - - /* - * The variables below keep track of how much state has been - * allocated while evaluating the script, so that it can be freed - * properly if an error occurs. - */ - - int gotParse = 0, objectsUsed = 0; - -#ifdef TCL_TIP280 - /* TIP #280 Structures for tracking of command locations. */ - CmdFrame eeFrame; - + int gotParse = 0; + unsigned int i, objectsUsed = 0; + /* These variables keep track of how much + * state has been allocated while evaluating + * the script, so that it can be freed + * properly if an error occurs. */ + Tcl_Parse *parsePtr = (Tcl_Parse *) + TclStackAlloc(interp, sizeof(Tcl_Parse)); + CmdFrame *eeFramePtr = (CmdFrame *) + TclStackAlloc(interp, sizeof(CmdFrame)); + Tcl_Obj **stackObjArray = (Tcl_Obj **) + TclStackAlloc(interp, minObjs * sizeof(Tcl_Obj *)); + int *expandStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int)); + int *linesStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int)); + /* TIP #280 Structures for tracking of command + * locations. */ /* * Pointer for the tracking of invisible continuation lines. Initialized * only if the caller gave us a table of locations to track, via @@ -4011,7 +4134,6 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) clNext = &iPtr->scriptCLLocPtr->loc[0]; } } -#endif if (numBytes < 0) { numBytes = strlen(script); @@ -4020,112 +4142,102 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) savedVarFramePtr = iPtr->varFramePtr; if (flags & TCL_EVAL_GLOBAL) { - iPtr->varFramePtr = NULL; + iPtr->varFramePtr = iPtr->rootFramePtr; } /* - * Each iteration through the following loop parses the next - * command from the script and then executes it. + * Each iteration through the following loop parses the next command from + * the script and then executes it. */ - objv = staticObjArray; + objv = objvSpace = stackObjArray; + lines = lineSpace = linesStack; + expand = expandStack; p = script; bytesLeft = numBytes; - if (iPtr->evalFlags & TCL_BRACKET_TERM) { - nested = 1; - } else { - nested = 0; - } -#ifdef TCL_TIP280 - /* TIP #280 Initialize tracking. Do not push on the frame stack yet. */ /* - * We may cont. counting based on a specific context (CTX), or open a new - * context, either for a sourced script, or 'eval'. For sourced files we - * always have a path object, even if nothing was specified in the interp - * itself. That makes code using it simpler as NULL checks can be left - * out. Sourced file without path in the 'scriptFile' is possible during - * Tcl initialization. + * TIP #280 Initialize tracking. Do not push on the frame stack yet. + * + * We may continue counting based on a specific context (CTX), or open a + * new context, either for a sourced script, or 'eval'. For sourced files + * we always have a path object, even if nothing was specified in the + * interp itself. That makes code using it simpler as NULL checks can be + * left out. Sourced file without path in the 'scriptFile' is possible + * during Tcl initialization. */ if (iPtr->evalFlags & TCL_EVAL_CTX) { - /* Path information comes out of the context. */ + /* + * Path information comes out of the context. + */ - eeFrame.type = TCL_LOCATION_SOURCE; - eeFrame.data.eval.path = iPtr->invokeCmdFramePtr->data.eval.path; - Tcl_IncrRefCount (eeFrame.data.eval.path); + eeFramePtr->type = TCL_LOCATION_SOURCE; + eeFramePtr->data.eval.path = iPtr->invokeCmdFramePtr->data.eval.path; + Tcl_IncrRefCount(eeFramePtr->data.eval.path); } else if (iPtr->evalFlags & TCL_EVAL_FILE) { - /* Set up for a sourced file */ + /* + * Set up for a sourced file. + */ - eeFrame.type = TCL_LOCATION_SOURCE; + eeFramePtr->type = TCL_LOCATION_SOURCE; if (iPtr->scriptFile) { - /* Normalization here, to have the correct pwd. Should have + /* + * Normalization here, to have the correct pwd. Should have * negligible impact on performance, as the norm should have been * done already by the 'source' invoking us, and it caches the - * result + * result. */ - Tcl_Obj* norm = Tcl_FSGetNormalizedPath (interp, iPtr->scriptFile); - if (!norm) { - /* Error message in the interp result */ - return TCL_ERROR; + Tcl_Obj *norm = Tcl_FSGetNormalizedPath(interp, iPtr->scriptFile); + + if (norm == NULL) { + /* + * Error message in the interp result. + */ + code = TCL_ERROR; + goto error; } - eeFrame.data.eval.path = norm; + eeFramePtr->data.eval.path = norm; } else { - eeFrame.data.eval.path = Tcl_NewStringObj ("",-1); + TclNewLiteralStringObj(eeFramePtr->data.eval.path, ""); } - Tcl_IncrRefCount (eeFrame.data.eval.path); + Tcl_IncrRefCount(eeFramePtr->data.eval.path); } else { - /* Set up for plain eval */ + /* + * Set up for plain eval. + */ - eeFrame.type = TCL_LOCATION_EVAL; - eeFrame.data.eval.path = NULL; + eeFramePtr->type = TCL_LOCATION_EVAL; + eeFramePtr->data.eval.path = NULL; } - eeFrame.level = (iPtr->cmdFramePtr == NULL - ? 1 - : iPtr->cmdFramePtr->level + 1); - eeFrame.framePtr = iPtr->framePtr; - eeFrame.nextPtr = iPtr->cmdFramePtr; - eeFrame.nline = 0; - eeFrame.line = NULL; -#endif + eeFramePtr->level = iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level + 1 : 1; + eeFramePtr->framePtr = iPtr->framePtr; + eeFramePtr->nextPtr = iPtr->cmdFramePtr; + eeFramePtr->nline = 0; + eeFramePtr->line = NULL; iPtr->evalFlags = 0; do { - if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse) - != TCL_OK) { + if (Tcl_ParseCommand(interp, p, bytesLeft, 0, parsePtr) != TCL_OK) { code = TCL_ERROR; goto error; } - gotParse = 1; - if (nested && parse.term == (script + numBytes)) { - /* - * A nested script can only terminate in ']'. If - * the parsing got terminated at the end of the script, - * there was no closing ']'. Report the syntax error. - */ - - code = TCL_ERROR; - goto error; - } - -#ifdef TCL_TIP280 /* * TIP #280 Track lines. The parser may have skipped text till it - * found the command we are now at. We have count the lines in this + * found the command we are now at. We have to count the lines in this * block, and do not forget invisible continuation lines. */ - TclAdvanceLines (&line, p, parse.commandStart); + TclAdvanceLines(&line, p, parsePtr->commandStart); TclAdvanceContinuations (&line, &clNext, - parse.commandStart - outerScript); -#endif + parsePtr->commandStart - outerScript); - if (parse.numWords > 0) { -#ifdef TCL_TIP280 + gotParse = 1; + if (parsePtr->numWords > 0) { /* * TIP #280. Track lines within the words of the current * command. We use a separate pointer into the table of @@ -4133,77 +4245,138 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) * per-command parsing. */ - int wordLine = line; - CONST char* wordStart = parse.commandStart; + int wordLine = line; + const char *wordStart = parsePtr->commandStart; int* wordCLNext = clNext; -#endif /* * Generate an array of objects for the words of the command. */ - - if (parse.numWords <= NUM_STATIC_OBJS) { - objv = staticObjArray; - } else { - objv = (Tcl_Obj **) ckalloc((unsigned) - (parse.numWords * sizeof (Tcl_Obj *))); - } -#ifdef TCL_TIP280 - eeFrame.nline = parse.numWords; - eeFrame.line = (int*) ckalloc((unsigned) - (parse.numWords * sizeof (int))); -#endif + unsigned int objectsNeeded = 0; + unsigned int numWords = parsePtr->numWords; - for (objectsUsed = 0, tokenPtr = parse.tokenPtr; - objectsUsed < parse.numWords; - objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) { -#ifndef TCL_TIP280 - code = Tcl_EvalTokensStandard(interp, tokenPtr+1, - tokenPtr->numComponents); -#else - /* - * TIP #280. Track lines to current word. Save the - * information on a per-word basis, signaling dynamic words as - * needed. Make the information available to the recursively - * called evaluator as well, including the type of context - * (source vs. eval). + if (numWords > minObjs) { + expand = (int *) ckalloc(numWords * sizeof(int)); + objvSpace = (Tcl_Obj **) + ckalloc(numWords * sizeof(Tcl_Obj *)); + lineSpace = (int *) ckalloc(numWords * sizeof(int)); + } + expandRequested = 0; + objv = objvSpace; + lines = lineSpace; + + for (objectsUsed = 0, tokenPtr = parsePtr->tokenPtr; + objectsUsed < numWords; + objectsUsed++, tokenPtr += tokenPtr->numComponents+1) { + /* + * TIP #280. Track lines to current word. Save the information + * on a per-word basis, signaling dynamic words as needed. + * Make the information available to the recursively called + * evaluator as well, including the type of context (source + * vs. eval). */ - TclAdvanceLines (&wordLine, wordStart, tokenPtr->start); + TclAdvanceLines(&wordLine, wordStart, tokenPtr->start); TclAdvanceContinuations (&wordLine, &wordCLNext, tokenPtr->start - outerScript); wordStart = tokenPtr->start; - eeFrame.line [objectsUsed] = (TclWordKnownAtCompileTime (tokenPtr) - ? wordLine - : -1); + lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL) + ? wordLine : -1; - if (eeFrame.type == TCL_LOCATION_SOURCE) { + if (eeFramePtr->type == TCL_LOCATION_SOURCE) { iPtr->evalFlags |= TCL_EVAL_FILE; } - code = EvalTokensStandard(interp, tokenPtr+1, - tokenPtr->numComponents, wordLine, - wordCLNext, outerScript); + code = TclSubstTokens(interp, tokenPtr+1, + tokenPtr->numComponents, NULL, wordLine, + wordCLNext, outerScript); iPtr->evalFlags = 0; -#endif - if (code == TCL_OK) { - objv[objectsUsed] = Tcl_GetObjResult(interp); - Tcl_IncrRefCount(objv[objectsUsed]); -#ifdef TCL_TIP280 - if (wordCLNext) { - TclContinuationsEnterDerived (objv[objectsUsed], - wordStart - outerScript, wordCLNext); + if (code != TCL_OK) { + goto error; + } + objv[objectsUsed] = Tcl_GetObjResult(interp); + Tcl_IncrRefCount(objv[objectsUsed]); + if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) { + int numElements; + + code = TclListObjLength(interp, objv[objectsUsed], + &numElements); + if (code == TCL_ERROR) { + /* + * Attempt to expand a non-list. + */ + + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (expanding word %d)", objectsUsed)); + Tcl_DecrRefCount(objv[objectsUsed]); + goto error; } -#endif + expandRequested = 1; + expand[objectsUsed] = 1; + + objectsNeeded += (numElements ? numElements : 1); } else { - goto error; + expand[objectsUsed] = 0; + objectsNeeded++; + } + + if (wordCLNext) { + TclContinuationsEnterDerived (objv[objectsUsed], + wordStart - outerScript, wordCLNext); + } + } /* for loop */ + if (expandRequested) { + /* + * Some word expansion was requested. Check for objv resize. + */ + + Tcl_Obj **copy = objvSpace; + int *lcopy = lineSpace; + int wordIdx = numWords; + int objIdx = objectsNeeded - 1; + + if ((numWords > minObjs) || (objectsNeeded > minObjs)) { + objv = objvSpace = (Tcl_Obj **) + ckalloc(objectsNeeded * sizeof(Tcl_Obj *)); + lines = lineSpace = (int *) + ckalloc(objectsNeeded * sizeof(int)); + } + + objectsUsed = 0; + while (wordIdx--) { + if (expand[wordIdx]) { + int numElements; + Tcl_Obj **elements, *temp = copy[wordIdx]; + + Tcl_ListObjGetElements(NULL, temp, &numElements, + &elements); + objectsUsed += numElements; + while (numElements--) { + lines[objIdx] = -1; + objv[objIdx--] = elements[numElements]; + Tcl_IncrRefCount(elements[numElements]); + } + Tcl_DecrRefCount(temp); + } else { + lines[objIdx] = lcopy[wordIdx]; + objv[objIdx--] = copy[wordIdx]; + objectsUsed++; + } + } + objv += objIdx+1; + + if (copy != stackObjArray) { + ckfree((char *) copy); + } + if (lcopy != linesStack) { + ckfree((char *) lcopy); } } - + /* * Execute the command and free the objects for its words. * @@ -4214,29 +4387,28 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) * have been executed. */ -#ifdef TCL_TIP280 - eeFrame.cmd.str.cmd = parse.commandStart; - eeFrame.cmd.str.len = parse.commandSize; + eeFramePtr->cmd.str.cmd = parsePtr->commandStart; + eeFramePtr->cmd.str.len = parsePtr->commandSize; - if (parse.term == parse.commandStart + parse.commandSize - 1) { - eeFrame.cmd.str.len --; + if (parsePtr->term == + parsePtr->commandStart + parsePtr->commandSize - 1) { + eeFramePtr->cmd.str.len--; } - TclArgumentEnter (interp, objv, objectsUsed, &eeFrame); - iPtr->cmdFramePtr = &eeFrame; -#endif - iPtr->numLevels++; - code = TclEvalObjvInternal(interp, objectsUsed, objv, - parse.commandStart, parse.commandSize, 0); + eeFramePtr->nline = objectsUsed; + eeFramePtr->line = lines; + + TclArgumentEnter (interp, objv, objectsUsed, eeFramePtr); + iPtr->cmdFramePtr = eeFramePtr; + iPtr->numLevels++; + code = TclEvalObjvInternal(interp, objectsUsed, objv, + parsePtr->commandStart, parsePtr->commandSize, 0); iPtr->numLevels--; -#ifdef TCL_TIP280 iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; TclArgumentRelease (interp, objv, objectsUsed); - ckfree ((char*) eeFrame.line); - eeFrame.line = NULL; - eeFrame.nline = 0; -#endif + eeFramePtr->line = NULL; + eeFramePtr->nline = 0; if (code != TCL_OK) { goto error; @@ -4245,9 +4417,21 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) Tcl_DecrRefCount(objv[i]); } objectsUsed = 0; - if (objv != staticObjArray) { - ckfree((char *) objv); - objv = staticObjArray; + if (objvSpace != stackObjArray) { + ckfree((char *) objvSpace); + objvSpace = stackObjArray; + ckfree((char *) lineSpace); + lineSpace = linesStack; + } + + /* + * Free expand separately since objvSpace could have been + * reallocated above. + */ + + if (expand != expandStack) { + ckfree((char *) expand); + expand = expandStack; } } @@ -4258,214 +4442,91 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) * executed command. */ - next = parse.commandStart + parse.commandSize; + next = parsePtr->commandStart + parsePtr->commandSize; bytesLeft -= next - p; p = next; -#ifdef TCL_TIP280 - TclAdvanceLines (&line, parse.commandStart, p); -#endif - Tcl_FreeParse(&parse); + TclAdvanceLines(&line, parsePtr->commandStart, p); + Tcl_FreeParse(parsePtr); gotParse = 0; - if (nested && (*parse.term == ']')) { - /* - * We get here in the special case where the TCL_BRACKET_TERM - * flag was set in the interpreter and the latest parsed command - * was terminated by the matching close-bracket we seek. - * Return immediately. - */ - - iPtr->termOffset = (p - 1) - script; - iPtr->varFramePtr = savedVarFramePtr; -#ifndef TCL_TIP280 - return TCL_OK; -#else - code = TCL_OK; - goto cleanup_return; -#endif - } } while (bytesLeft > 0); - - if (nested) { - /* - * This nested script did not terminate in ']', it is an error. - */ - - code = TCL_ERROR; - goto error; - } - - iPtr->termOffset = p - script; iPtr->varFramePtr = savedVarFramePtr; -#ifndef TCL_TIP280 - return TCL_OK; -#else code = TCL_OK; goto cleanup_return; -#endif - error: + error: /* - * Generate various pieces of error information, such as the line - * number where the error occurred and information to add to the - * errorInfo variable. Then free resources that had been allocated - * to the command. + * Generate and log various pieces of error information. */ if (iPtr->numLevels == 0) { if (code == TCL_RETURN) { code = TclUpdateReturnInfo(iPtr); } - if ((code != TCL_OK) && (code != TCL_ERROR) - && !allowExceptions) { + if ((code != TCL_OK) && (code != TCL_ERROR) && !allowExceptions) { ProcessUnexpectedResult(interp, code); code = TCL_ERROR; } } - if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { - commandLength = parse.commandSize; - if (parse.term == parse.commandStart + commandLength - 1) { + if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { + commandLength = parsePtr->commandSize; + if (parsePtr->term == parsePtr->commandStart + commandLength - 1) { /* * The terminator character (such as ; or ]) of the command where * the error occurred is the last character in the parsed command. * Reduce the length by one so that the error message doesn't * include the terminator character. */ - + commandLength -= 1; } - Tcl_LogCommandInfo(interp, script, parse.commandStart, commandLength); + Tcl_LogCommandInfo(interp, script, parsePtr->commandStart, + commandLength); } - + iPtr->flags &= ~ERR_ALREADY_LOGGED; + + /* + * Then free resources that had been allocated to the command. + */ + for (i = 0; i < objectsUsed; i++) { Tcl_DecrRefCount(objv[i]); } if (gotParse) { - Tcl_FreeParse(&parse); + Tcl_FreeParse(parsePtr); } - if (objv != staticObjArray) { - ckfree((char *) objv); + if (objvSpace != stackObjArray) { + ckfree((char *) objvSpace); + ckfree((char *) lineSpace); } - iPtr->varFramePtr = savedVarFramePtr; - - /* - * All that's left to do before returning is to set iPtr->termOffset - * to point past the end of the script we just evaluated. - */ - - next = parse.commandStart + parse.commandSize; - bytesLeft -= next - p; - p = next; - - if (!nested) { - iPtr->termOffset = p - script; -#ifndef TCL_TIP280 - return code; -#else - goto cleanup_return; -#endif + if (expand != expandStack) { + ckfree((char *) expand); } + iPtr->varFramePtr = savedVarFramePtr; + cleanup_return: /* - * When we are nested (the TCL_BRACKET_TERM flag was set in the - * interpreter), we must find the matching close-bracket to - * end the script we are evaluating. - * - * When our return code is TCL_CONTINUE or TCL_RETURN, we want - * to correctly set iPtr->termOffset to point to that matching - * close-bracket so our caller can move to the part of the - * string beyond the script we were asked to evaluate. - * So we try to parse past the rest of the commands. + * TIP #280. Release the local CmdFrame, and its contents. */ - next = NULL; - while (bytesLeft && (*parse.term != ']')) { - if (TCL_OK != Tcl_ParseCommand(NULL, p, bytesLeft, 1, &parse)) { - /* - * Syntax error. Set the termOffset to the beginning of - * the last command parsed. - */ - - if (next == NULL) { - iPtr->termOffset = (parse.commandStart - 1) - script; - } else { - iPtr->termOffset = (next - 1) - script; - } -#ifndef TCL_TIP280 - return code; -#else - goto cleanup_return; -#endif - } - next = parse.commandStart + parse.commandSize; - bytesLeft -= next - p; - p = next; - next = parse.commandStart; - Tcl_FreeParse(&parse); + if (eeFramePtr->type == TCL_LOCATION_SOURCE) { + Tcl_DecrRefCount(eeFramePtr->data.eval.path); } + TclStackFree(interp, linesStack); + TclStackFree(interp, expandStack); + TclStackFree(interp, stackObjArray); + TclStackFree(interp, eeFramePtr); + TclStackFree(interp, parsePtr); - if (bytesLeft) { - /* - * parse.term points to the close-bracket. - */ - - iPtr->termOffset = parse.term - script; - } else if (parse.term == script + numBytes) { - /* - * There was no close-bracket. Syntax error. - */ - - iPtr->termOffset = parse.term - script; - Tcl_SetObjResult(interp, - Tcl_NewStringObj("missing close-bracket", -1)); -#ifndef TCL_TIP280 - return TCL_ERROR; -#else - code = TCL_ERROR; - goto cleanup_return; -#endif - } else if (*parse.term != ']') { - /* - * There was no close-bracket. Syntax error. - */ - - iPtr->termOffset = (parse.term + 1) - script; - Tcl_SetObjResult(interp, - Tcl_NewStringObj("missing close-bracket", -1)); -#ifndef TCL_TIP280 - return TCL_ERROR; -#else - code = TCL_ERROR; - goto cleanup_return; -#endif - } else { - /* - * parse.term points to the close-bracket. - */ - iPtr->termOffset = parse.term - script; - } - -#ifdef TCL_TIP280 - cleanup_return: - /* TIP #280. Release the local CmdFrame, and its contents. */ - - if (eeFrame.line != NULL) { - ckfree ((char*) eeFrame.line); - } - if (eeFrame.type == TCL_LOCATION_SOURCE) { - Tcl_DecrRefCount (eeFrame.data.eval.path); - } -#endif return code; } -#ifdef TCL_TIP280 /* *---------------------------------------------------------------------- * * TclAdvanceLines -- * - * This procedure is a helper which counts the number of lines - * in a block of text and advances an external counter. + * This function is a helper which counts the number of lines in a block + * of text and advances an external counter. * * Results: * None. @@ -4478,15 +4539,16 @@ EvalEx(interp, script, numBytes, flags, line, clNextOuter, outerScript) */ void -TclAdvanceLines (line,start,end) - int* line; - CONST char* start; - CONST char* end; +TclAdvanceLines( + int *line, + const char *start, + const char *end) { - CONST char* p; + register const char *p; + for (p = start; p < end; p++) { - if (*p == '\n') { - (*line) ++; + if (*p == '\n') { + (*line)++; } } } @@ -4520,11 +4582,11 @@ TclAdvanceContinuations (line,clNextPtrPtr,loc) /* * Track the invisible continuation lines embedded in a script, if * any. Here they are just spaces (already). They were removed by - * EvalTokensStandard() via TclParseBackslash(). + * TclSubstTokens() via TclParseBackslash(). * - * *clNextPtrPtr <=> We have continuation lines to track. - * **clNextPtrPtr >= 0 <=> We are not beyond the last possible location. - * loc >= **clNextPtrPtr <=> We stepped beyond the current cont. line. + * *clNextPtrPtr <=> We have continuation lines to track. + * **clNextPtrPtr >= 0 <=> We are not beyond the last possible location. + * loc >= **clNextPtrPtr <=> We stepped beyond the current cont. line. */ while (*clNextPtrPtr && (**clNextPtrPtr >= 0) && (loc >= **clNextPtrPtr)) { @@ -4663,7 +4725,7 @@ TclArgumentRelease(interp,objv,objc) * * This procedure is a helper for the TIP #280 uplevel extension. * It enters location references for the literal arguments of commands - * in bytecode about to be executed. Only the first entry has the actual + * in bytecode about to be invoked. Only the first entry has the actual * data, further entries simply count the usage up. * * Results: @@ -4677,7 +4739,7 @@ TclArgumentRelease(interp,objv,objc) */ void -TclArgumentBCEnter(interp, objv, objc, codePtr, cfPtr, pc) +TclArgumentBCEnter(interp,objv,objc,codePtr,cfPtr,pc) Tcl_Interp* interp; Tcl_Obj* objv[]; int objc; @@ -4690,12 +4752,12 @@ TclArgumentBCEnter(interp, objv, objc, codePtr, cfPtr, pc) if (hePtr) { ExtCmdLoc* eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hePtr); - hePtr = Tcl_FindHashEntry(&eclPtr->litInfo, (char*) pc); + hePtr = Tcl_FindHashEntry(&eclPtr->litInfo, INT2PTR(pc)); if (hePtr) { - int word; - int cmd = (int) Tcl_GetHashValue(hePtr); + int cmd = PTR2INT(Tcl_GetHashValue(hePtr)); ECL* ePtr = &eclPtr->loc[cmd]; + int word; /* * A few truths ... @@ -4707,6 +4769,10 @@ TclArgumentBCEnter(interp, objv, objc, codePtr, cfPtr, pc) * have to save them at compile time. */ + if (ePtr->nline != objc) { + Tcl_Panic ("TIP 280 data structure inconsistency"); + } + for (word = 1; word < objc; word++) { if (ePtr->line[word] >= 0) { int isnew; @@ -4763,7 +4829,7 @@ TclArgumentBCEnter(interp, objv, objc, codePtr, cfPtr, pc) */ void -TclArgumentBCRelease(interp, objv, objc, codePtr, pc) +TclArgumentBCRelease(interp,objv,objc,codePtr,pc) Tcl_Interp* interp; Tcl_Obj* objv[]; int objc; @@ -4775,10 +4841,10 @@ TclArgumentBCRelease(interp, objv, objc, codePtr, pc) if (hePtr) { ExtCmdLoc* eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hePtr); - hePtr = Tcl_FindHashEntry(&eclPtr->litInfo, (char*) pc); + hePtr = Tcl_FindHashEntry(&eclPtr->litInfo, INT2PTR(pc)); if (hePtr) { - int cmd = (int) Tcl_GetHashValue(hePtr); + int cmd = PTR2INT(Tcl_GetHashValue(hePtr)); ECL* ePtr = &eclPtr->loc[cmd]; int word; @@ -4792,7 +4858,7 @@ TclArgumentBCRelease(interp, objv, objc, codePtr, pc) (char *) objv[word]); if (hPtr) { CFWordBC* cfwPtr = (CFWordBC*) Tcl_GetHashValue (hPtr); - + if (cfwPtr->prevPtr) { Tcl_SetHashValue(hPtr, cfwPtr->prevPtr); } else { @@ -4837,16 +4903,16 @@ TclArgumentGet(interp,obj,cfPtrPtr,wordPtr) CmdFrame* framePtr; /* - * An object which either has no string rep guaranteed to have been - * generated dynamically: bail out, this cannot have a usable absolute - * location. _Do not touch_ the information the set up by the caller. It - * knows better than us. + * An object which either has no string rep or else is a canonical list is + * guaranteed to have been generated dynamically: bail out, this cannot + * have a usable absolute location. _Do not touch_ the information the set + * up by the caller. It knows better than us. */ - if (!obj->bytes) { + if ((obj->bytes == NULL) || TclListObjIsCanonical(obj)) { return; } - + /* * First look for location information recorded in the argument * stack. That is nearest. @@ -4866,36 +4932,34 @@ TclArgumentGet(interp,obj,cfPtrPtr,wordPtr) */ hPtr = Tcl_FindHashEntry (iPtr->lineLABCPtr, (char *) obj); + if (hPtr) { CFWordBC* cfwPtr = (CFWordBC*) Tcl_GetHashValue (hPtr); framePtr = cfwPtr->framePtr; - framePtr->data.tebc.pc = (char*) ((ByteCode*) - framePtr->data.tebc.codePtr)->codeStart + cfwPtr->pc; + framePtr->data.tebc.pc = (char *) (((ByteCode*) + framePtr->data.tebc.codePtr)->codeStart + cfwPtr->pc); *cfPtrPtr = cfwPtr->framePtr; *wordPtr = cfwPtr->word; return; } } -#endif /* *---------------------------------------------------------------------- * * Tcl_Eval -- * - * Execute a Tcl command in a string. This procedure executes the - * script directly, rather than compiling it to bytecodes. Before - * the arrival of the bytecode compiler in Tcl 8.0 Tcl_Eval was - * the main procedure used for executing Tcl commands, but nowadays - * it isn't used much. + * Execute a Tcl command in a string. This function executes the script + * directly, rather than compiling it to bytecodes. Before the arrival of + * the bytecode compiler in Tcl 8.0 Tcl_Eval was the main function used + * for executing Tcl commands, but nowadays it isn't used much. * * Results: - * The return value is one of the return codes defined in tcl.h - * (such as TCL_OK), and interp's result contains a value - * to supplement the return code. The value of the result - * will persist only until the next call to Tcl_Eval or Tcl_EvalObj: - * you must copy it or lose it! + * The return value is one of the return codes defined in tcl.h (such as + * TCL_OK), and interp's result contains a value to supplement the return + * code. The value of the result will persist only until the next call to + * Tcl_Eval or Tcl_EvalObj: you must copy it or lose it! * * Side effects: * Can be almost arbitrary, depending on the commands in the script. @@ -4904,21 +4968,20 @@ TclArgumentGet(interp,obj,cfPtrPtr,wordPtr) */ int -Tcl_Eval(interp, string) - Tcl_Interp *interp; /* Token for command interpreter (returned - * by previous call to Tcl_CreateInterp). */ - CONST char *string; /* Pointer to TCL command to execute. */ +Tcl_Eval( + Tcl_Interp *interp, /* Token for command interpreter (returned by + * previous call to Tcl_CreateInterp). */ + const char *script) /* Pointer to TCL command to execute. */ { - int code = Tcl_EvalEx(interp, string, -1, 0); + int code = Tcl_EvalEx(interp, script, -1, 0); /* - * For backwards compatibility with old C code that predates the - * object system in Tcl 8.0, we have to mirror the object result - * back into the string result (some callers may expect it there). + * For backwards compatibility with old C code that predates the object + * system in Tcl 8.0, we have to mirror the object result back into the + * string result (some callers may expect it there). */ - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); + (void) Tcl_GetStringResult(interp); return code; } @@ -4941,18 +5004,18 @@ Tcl_Eval(interp, string) #undef Tcl_EvalObj int -Tcl_EvalObj(interp, objPtr) - Tcl_Interp * interp; - Tcl_Obj * objPtr; +Tcl_EvalObj( + Tcl_Interp *interp, + Tcl_Obj *objPtr) { return Tcl_EvalObjEx(interp, objPtr, 0); } #undef Tcl_GlobalEvalObj int -Tcl_GlobalEvalObj(interp, objPtr) - Tcl_Interp * interp; - Tcl_Obj * objPtr; +Tcl_GlobalEvalObj( + Tcl_Interp *interp, + Tcl_Obj *objPtr) { return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL); } @@ -4963,322 +5026,272 @@ Tcl_GlobalEvalObj(interp, objPtr) * Tcl_EvalObjEx, TclEvalObjEx -- * * Execute Tcl commands stored in a Tcl object. These commands are - * compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT - * is specified. + * compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT is + * specified. * * Results: - * The return value is one of the return codes defined in tcl.h - * (such as TCL_OK), and the interpreter's result contains a value - * to supplement the return code. + * The return value is one of the return codes defined in tcl.h (such as + * TCL_OK), and the interpreter's result contains a value to supplement + * the return code. * * Side effects: - * The object is converted, if necessary, to a ByteCode object that - * holds the bytecode instructions for the commands. Executing the - * commands will almost certainly have side effects that depend - * on those commands. - * - * Just as in Tcl_Eval, interp->termOffset is set to the offset of the - * last character executed in the objPtr's string. + * The object is converted, if necessary, to a ByteCode object that holds + * the bytecode instructions for the commands. Executing the commands + * will almost certainly have side effects that depend on those commands. * * TIP #280 : Keep public API, internally extended API. *---------------------------------------------------------------------- */ int -Tcl_EvalObjEx(interp, objPtr, flags) - Tcl_Interp *interp; /* Token for command interpreter - * (returned by a previous call to - * Tcl_CreateInterp). */ - register Tcl_Obj *objPtr; /* Pointer to object containing - * commands to execute. */ - int flags; /* Collection of OR-ed bits that - * control the evaluation of the - * script. Supported values are - * TCL_EVAL_GLOBAL and - * TCL_EVAL_DIRECT. */ +Tcl_EvalObjEx( + Tcl_Interp *interp, /* Token for command interpreter (returned by + * a previous call to Tcl_CreateInterp). */ + register Tcl_Obj *objPtr, /* Pointer to object containing commands to + * execute. */ + int flags) /* Collection of OR-ed bits that control the + * evaluation of the script. Supported values + * are TCL_EVAL_GLOBAL and TCL_EVAL_DIRECT. */ { -#ifdef TCL_TIP280 - return TclEvalObjEx (interp, objPtr, flags, NULL, 0); + return TclEvalObjEx(interp, objPtr, flags, NULL, 0); } int -TclEvalObjEx(interp, objPtr, flags, invoker, word) - Tcl_Interp *interp; /* Token for command interpreter - * (returned by a previous call to - * Tcl_CreateInterp). */ - register Tcl_Obj *objPtr; /* Pointer to object containing - * commands to execute. */ - int flags; /* Collection of OR-ed bits that - * control the evaluation of the - * script. Supported values are - * TCL_EVAL_GLOBAL and - * TCL_EVAL_DIRECT. */ - CONST CmdFrame* invoker; /* Frame of the command doing the eval */ - int word; /* Index of the word which is in objPtr */ +TclEvalObjEx( + Tcl_Interp *interp, /* Token for command interpreter (returned by + * a previous call to Tcl_CreateInterp). */ + register Tcl_Obj *objPtr, /* Pointer to object containing commands to + * execute. */ + int flags, /* Collection of OR-ed bits that control the + * evaluation of the script. Supported values + * are TCL_EVAL_GLOBAL and TCL_EVAL_DIRECT. */ + const CmdFrame *invoker, /* Frame of the command doing the eval. */ + int word) /* Index of the word which is in objPtr. */ { -#endif register Interp *iPtr = (Interp *) interp; char *script; int numSrcBytes; int result; - CallFrame *savedVarFramePtr; /* Saves old copy of iPtr->varFramePtr - * in case TCL_EVAL_GLOBAL was set. */ - int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); + CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case + * TCL_EVAL_GLOBAL was set. */ Tcl_IncrRefCount(objPtr); - if ((iPtr->flags & USE_EVAL_DIRECT) || (flags & TCL_EVAL_DIRECT)) { + /* Pure List Optimization (no string representation). In this case, we can + * safely use Tcl_EvalObjv instead and get an appreciable improvement in + * execution speed. This is because it allows us to avoid a setFromAny + * step that would just pack everything into a string and back out again. + * + * This also preserves any associations between list elements and location + * information for such elements. + * + * This restriction has been relaxed a bit by storing in lists whether + * they are "canonical" or not (a canonical list being one that is either + * pure or that has its string rep derived by UpdateStringOfList from the + * internal rep). + */ + + if (TclListObjIsCanonical(objPtr)) { + /* + * TIP #280 Structures for tracking lines. As we know that this is + * dynamic execution we ignore the invoker, even if known. + */ + + int nelements; + Tcl_Obj **elements, *copyPtr = TclListObjCopy(NULL, objPtr); + CmdFrame *eoFramePtr = (CmdFrame *) + TclStackAlloc(interp, sizeof(CmdFrame)); + + eoFramePtr->type = TCL_LOCATION_EVAL_LIST; + eoFramePtr->level = (iPtr->cmdFramePtr == NULL? 1 + : iPtr->cmdFramePtr->level + 1); + eoFramePtr->framePtr = iPtr->framePtr; + eoFramePtr->nextPtr = iPtr->cmdFramePtr; + + eoFramePtr->nline = 0; + eoFramePtr->line = NULL; + + eoFramePtr->cmd.listPtr = objPtr; + Tcl_IncrRefCount(eoFramePtr->cmd.listPtr); + eoFramePtr->data.eval.path = NULL; + + /* + * TIP #280 We do _not_ compute all the line numbers for the words + * in the command. For the eval of a pure list the most sensible + * choice is to put all words on line 1. Given that we neither + * need memory for them nor compute anything. 'line' is left + * NULL. The two places using this information (TclInfoFrame, and + * TclInitCompileEnv), are special-cased to use the proper line + * number directly instead of accessing the 'line' array. + */ + + Tcl_ListObjGetElements(NULL, copyPtr, &nelements, &elements); + + iPtr->cmdFramePtr = eoFramePtr; + result = Tcl_EvalObjv(interp, nelements, elements, flags); + + Tcl_DecrRefCount(copyPtr); + iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; + Tcl_DecrRefCount(eoFramePtr->cmd.listPtr); + TclStackFree(interp, eoFramePtr); + } else if (flags & TCL_EVAL_DIRECT) { /* * We're not supposed to use the compiler or byte-code interpreter. - * Let Tcl_EvalEx evaluate the command directly (and probably - * more slowly). + * Let Tcl_EvalEx evaluate the command directly (and probably more + * slowly). + */ + + /* + * TIP #280. Propagate context as much as we can. Especially if the + * script to evaluate is a single literal it makes sense to look if + * our context is one with absolute line numbers we can then track + * into the literal itself too. + * + * See also tclCompile.c, TclInitCompileEnv, for the equivalent code + * in the bytecode compiler. + */ + + /* + * Now we check if we have data about invisible continuation lines for + * the script, and make it available to the direct script parser and + * evaluator we are about to call, if so. * - * Pure List Optimization (no string representation). In this - * case, we can safely use Tcl_EvalObjv instead and get an - * appreciable improvement in execution speed. This is because it - * allows us to avoid a setFromAny step that would just pack - * everything into a string and back out again. + * It may be possible that the script Tcl_Obj* can be free'd while the + * evaluator is using it, leading to the release of the associated + * ContLineLoc structure as well. To ensure that the latter doesn't + * happen we set a lock on it. We release this lock later in this + * function, after the evaluator is done. The relevant "lineCLPtr" + * hashtable is managed in the file "tclObj.c". * - * USE_EVAL_DIRECT is a special flag used for testing purpose only - * (ensure we go into the TCL_EVAL_DIRECT path, avoiding opt) + * Another important action is to save (and later restore) the + * continuation line information of the caller, in case we are + * executing nested commands in the eval/direct path. */ - if (!(iPtr->flags & USE_EVAL_DIRECT) && - (objPtr->typePtr == &tclListType) && /* is a list... */ - (objPtr->bytes == NULL) /* ...without a string rep */) { - register List *listRepPtr = - (List *) objPtr->internalRep.twoPtrValue.ptr1; - int i, objc = listRepPtr->elemCount; - -#define TEOE_PREALLOC 10 - Tcl_Obj *staticObjv[TEOE_PREALLOC], **objv = staticObjv; - -#ifdef TCL_TIP280 - /* TIP #280 Structures for tracking lines. - * As we know that this is dynamic execution we ignore the - * invoker, even if known. - */ - CmdFrame eoFrame; - - eoFrame.type = TCL_LOCATION_EVAL_LIST; - eoFrame.level = (iPtr->cmdFramePtr == NULL ? - 1 : - iPtr->cmdFramePtr->level + 1); - eoFrame.framePtr = iPtr->framePtr; - eoFrame.nextPtr = iPtr->cmdFramePtr; - eoFrame.nline = 0; - eoFrame.line = NULL; - - /* NOTE: Getting the string rep of the list to eval to fill the - * command information required by 'info frame' implies that - * further calls for the same list would not be optimized, as it - * would not be 'pure' anymore. It would also be a waste of time - * as most of the time this information is not needed at all. What - * we do instead is to keep the list obj itself around and have - * 'info frame' sort it out. - */ - eoFrame.cmd.listPtr = objPtr; - Tcl_IncrRefCount (eoFrame.cmd.listPtr); - eoFrame.data.eval.path = NULL; -#endif - if (objc > TEOE_PREALLOC) { - objv = (Tcl_Obj **) ckalloc(objc*sizeof(Tcl_Obj *)); - } -#undef TEOE_PREALLOC - /* - * Copy the list elements here, to avoid a segfault if - * objPtr loses its List internal rep [Bug 1119369]. - * - * TIP #280 We do _not_ compute all the line numbers for the words - * in the command. For the eval of a pure list the most sensible - * choice is to put all words on line 1. Given that we neither - * need memory for them nor compute anything. 'line' is left - * NULL. The two places using this information (TclInfoFrame, and - * TclInitCompileEnv), are special-cased to use the proper line - * number directly instead of accessing the 'line' array. - */ + ContLineLoc* saveCLLocPtr = iPtr->scriptCLLocPtr; + ContLineLoc* clLocPtr = TclContinuationsGet (objPtr); - for (i=0; i < objc; i++) { - objv[i] = listRepPtr->elements[i]; - Tcl_IncrRefCount(objv[i]); - } + if (clLocPtr) { + iPtr->scriptCLLocPtr = clLocPtr; + Tcl_Preserve (iPtr->scriptCLLocPtr); + } else { + iPtr->scriptCLLocPtr = NULL; + } -#ifdef TCL_TIP280 - iPtr->cmdFramePtr = &eoFrame; -#endif - result = Tcl_EvalObjv(interp, objc, objv, flags); -#ifdef TCL_TIP280 - iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; - Tcl_DecrRefCount (eoFrame.cmd.listPtr); -#endif + if (invoker == NULL) { + /* + * No context, force opening of our own. + */ - for (i=0; i < objc; i++) { - TclDecrRefCount(objv[i]); - } - if (objv != staticObjv) { - ckfree((char *) objv); - } -#ifdef TCL_TIP280 - ckfree ((char*) eoFrame.line); - eoFrame.line = NULL; - eoFrame.nline = 0; -#endif - } else { -#ifndef TCL_TIP280 script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); result = Tcl_EvalEx(interp, script, numSrcBytes, flags); -#else + } else { /* - * TIP #280. Propagate context as much as we can. Especially if - * the script to evaluate is a single literal it makes sense to - * look if our context is one with absolute line numbers we can - * then track into the literal itself too. + * We have an invoker, describing the command asking for the + * evaluation of a subordinate script. This script may originate + * in a literal word, or from a variable, etc. Using the line + * array we now check if we have good line information for the + * relevant word. The type of context is relevant as well. In a + * non-'source' context we don't have to try tracking lines. * - * See also tclCompile.c, TclInitCompileEnv, for the equivalent - * code in the bytecode compiler. + * First see if the word exists and is a literal. If not we go + * through the easy dynamic branch. No need to perform more + * complex invokations. */ - /* - * Now we check if we have data about invisible continuation lines - * for the script, and make it available to the direct script - * parser and evaluator we are about to call, if so. - * - * It may be possible that the script Tcl_Obj* can be free'd while - * the evaluator is using it, leading to the release of the - * associated ContLineLoc structure as well. To ensure that the - * latter doesn't happen we set a lock on it. We release this lock - * later in this function, after the evaluator is done. The - * relevant "lineCLPtr" hashtable is managed in the file - * "tclObj.c". - * - * Another important action is to save (and later restore) the - * continuation line information of the caller, in case we are - * executing nested commands in the eval/direct path. - */ + int pc = 0; + CmdFrame *ctxPtr = (CmdFrame *) + TclStackAlloc(interp, sizeof(CmdFrame)); - ContLineLoc* saveCLLocPtr = iPtr->scriptCLLocPtr; - ContLineLoc* clLocPtr = TclContinuationsGet (objPtr); + *ctxPtr = *invoker; + if (invoker->type == TCL_LOCATION_BC) { + /* + * Note: Type BC => ctxPtr->data.eval.path is not used. + * ctxPtr->data.tebc.codePtr is used instead. + */ - if (clLocPtr) { - iPtr->scriptCLLocPtr = clLocPtr; - Tcl_Preserve (iPtr->scriptCLLocPtr); - } else { - iPtr->scriptCLLocPtr = NULL; + TclGetSrcInfoForPc(ctxPtr); + pc = 1; } - if (invoker == NULL) { - /* No context, force opening of our own */ - script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); - result = Tcl_EvalEx(interp, script, numSrcBytes, flags); - } else { - /* We have an invoker, describing the command asking for the - * evaluation of a subordinate script. This script may - * originate in a literal word, or from a variable, etc. Using - * the line array we now check if we have good line - * information for the relevant word. The type of context is - * relevant as well. In a non-'source' context we don't have - * to try tracking lines. - * - * First see if the word exists and is a literal. If not we go - * through the easy dynamic branch. No need to perform more - * complex invokations. - */ + script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); - CmdFrame ctx = *invoker; - int pc = 0; + if ((ctxPtr->nline <= word) || + (ctxPtr->line[word] < 0) || + (ctxPtr->type != TCL_LOCATION_SOURCE)) { + /* + * Dynamic script, or dynamic context, force our own + * context. + */ - if (invoker->type == TCL_LOCATION_BC) { - /* Note: Type BC => ctx.data.eval.path is not used. - * ctx.data.tebc.codePtr is used instead. - */ - TclGetSrcInfoForPc (&ctx); - pc = 1; - } + result = Tcl_EvalEx(interp, script, numSrcBytes, flags); - script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); + } else { + /* + * Absolute context to reuse. + */ - if ((ctx.nline <= word) || - (ctx.line[word] < 0) || - (ctx.type != TCL_LOCATION_SOURCE)) { - /* Dynamic script, or dynamic context, force our own - * context */ + iPtr->invokeCmdFramePtr = ctxPtr; + iPtr->evalFlags |= TCL_EVAL_CTX; - result = Tcl_EvalEx(interp, script, numSrcBytes, flags); - } else { - /* Absolute context available to reuse. */ + result = TclEvalEx(interp, script, numSrcBytes, flags, + ctxPtr->line[word], NULL, script); + } - iPtr->invokeCmdFramePtr = &ctx; - iPtr->evalFlags |= TCL_EVAL_CTX; + if (pc && (ctxPtr->type == TCL_LOCATION_SOURCE)) { + /* + * Death of SrcInfo reference. + */ - result = EvalEx(interp, script, numSrcBytes, flags, - ctx.line [word], NULL, script); - } - if (pc && (ctx.type == TCL_LOCATION_SOURCE)) { - /* Death of SrcInfo reference. */ - Tcl_DecrRefCount(ctx.data.eval.path); - } + Tcl_DecrRefCount(ctxPtr->data.eval.path); } + TclStackFree(interp, ctxPtr); + } - /* - * Now release the lock on the continuation line information, if - * any, and restore the caller's settings. - */ + /* + * Now release the lock on the continuation line information, if + * any, and restore the caller's settings. + */ - if (iPtr->scriptCLLocPtr) { - Tcl_Release (iPtr->scriptCLLocPtr); - } - iPtr->scriptCLLocPtr = saveCLLocPtr; -#endif + if (iPtr->scriptCLLocPtr) { + Tcl_Release (iPtr->scriptCLLocPtr); } + iPtr->scriptCLLocPtr = saveCLLocPtr; } else { /* * Let the compiler/engine subsystem do the evaluation. * - * TIP #280 The invoker provides us with the context for the - * script. We transfer this to the byte code compiler. + * TIP #280 The invoker provides us with the context for the script. + * We transfer this to the byte code compiler. */ + int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); savedVarFramePtr = iPtr->varFramePtr; if (flags & TCL_EVAL_GLOBAL) { - iPtr->varFramePtr = NULL; + iPtr->varFramePtr = iPtr->rootFramePtr; } -#ifndef TCL_TIP280 - result = TclCompEvalObj(interp, objPtr); -#else result = TclCompEvalObj(interp, objPtr, invoker, word); -#endif /* - * If we are again at the top level, process any unusual - * return code returned by the evaluated code. + * If we are again at the top level, process any unusual return code + * returned by the evaluated code. */ - + if (iPtr->numLevels == 0) { if (result == TCL_RETURN) { result = TclUpdateReturnInfo(iPtr); } - if ((result != TCL_OK) && (result != TCL_ERROR) - && !allowExceptions) { + if ((result != TCL_OK) && (result != TCL_ERROR) + && !allowExceptions) { ProcessUnexpectedResult(interp, result); result = TCL_ERROR; - - /* - * If an error was created here, record information about - * what was being executed when the error occurred. Remove - * the extra \n added by tclMain.c in the command sent to - * Tcl_LogCommandInfo [Bug 833150]. - */ - - if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { - script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); - Tcl_LogCommandInfo(interp, script, script, --numSrcBytes); - iPtr->flags &= ~ERR_ALREADY_LOGGED; - } + script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); + Tcl_LogCommandInfo(interp, script, script, numSrcBytes); } } iPtr->evalFlags = 0; - iPtr->varFramePtr = savedVarFramePtr; + iPtr->varFramePtr = savedVarFramePtr; } TclDecrRefCount(objPtr); @@ -5290,39 +5303,37 @@ TclEvalObjEx(interp, objPtr, flags, invoker, word) * * ProcessUnexpectedResult -- * - * Procedure called by Tcl_EvalObj to set the interpreter's result - * value to an appropriate error message when the code it evaluates - * returns an unexpected result code (not TCL_OK and not TCL_ERROR) to - * the topmost evaluation level. + * Function called by Tcl_EvalObj to set the interpreter's result value + * to an appropriate error message when the code it evaluates returns an + * unexpected result code (not TCL_OK and not TCL_ERROR) to the topmost + * evaluation level. * * Results: * None. * * Side effects: - * The interpreter result is set to an error message appropriate to - * the result code. + * The interpreter result is set to an error message appropriate to the + * result code. * *---------------------------------------------------------------------- */ static void -ProcessUnexpectedResult(interp, returnCode) - Tcl_Interp *interp; /* The interpreter in which the unexpected +ProcessUnexpectedResult( + Tcl_Interp *interp, /* The interpreter in which the unexpected * result code was returned. */ - int returnCode; /* The unexpected result code. */ + int returnCode) /* The unexpected result code. */ { Tcl_ResetResult(interp); if (returnCode == TCL_BREAK) { - Tcl_AppendToObj(Tcl_GetObjResult(interp), - "invoked \"break\" outside of a loop", -1); + Tcl_AppendResult(interp, + "invoked \"break\" outside of a loop", NULL); } else if (returnCode == TCL_CONTINUE) { - Tcl_AppendToObj(Tcl_GetObjResult(interp), - "invoked \"continue\" outside of a loop", -1); + Tcl_AppendResult(interp, + "invoked \"continue\" outside of a loop", NULL); } else { - char buf[30 + TCL_INTEGER_SPACE]; - - sprintf(buf, "command returned bad code: %d", returnCode); - Tcl_SetResult(interp, buf, TCL_VOLATILE); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "command returned bad code: %d", returnCode)); } } @@ -5331,15 +5342,15 @@ ProcessUnexpectedResult(interp, returnCode) * * Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBoolean -- * - * Procedures to evaluate an expression and return its value in a + * Functions to evaluate an expression and return its value in a * particular form. * * Results: - * Each of the procedures below returns a standard Tcl result. If an - * error occurs then an error message is left in the interp's result. - * Otherwise the value of the expression, in the appropriate form, - * is stored at *ptr. If the expression had a result that was - * incompatible with the desired form then an error is returned. + * Each of the functions below returns a standard Tcl result. If an error + * occurs then an error message is left in the interp's result. Otherwise + * the value of the expression, in the appropriate form, is stored at + * *ptr. If the expression had a result that was incompatible with the + * desired form then an error is returned. * * Side effects: * None. @@ -5348,197 +5359,92 @@ ProcessUnexpectedResult(interp, returnCode) */ int -Tcl_ExprLong(interp, string, ptr) - Tcl_Interp *interp; /* Context in which to evaluate the +Tcl_ExprLong( + Tcl_Interp *interp, /* Context in which to evaluate the * expression. */ - CONST char *string; /* Expression to evaluate. */ - long *ptr; /* Where to store result. */ + const char *exprstring, /* Expression to evaluate. */ + long *ptr) /* Where to store result. */ { register Tcl_Obj *exprPtr; - Tcl_Obj *resultPtr; - int length = strlen(string); int result = TCL_OK; - - if (length > 0) { - exprPtr = Tcl_NewStringObj(string, length); - Tcl_IncrRefCount(exprPtr); - result = Tcl_ExprObj(interp, exprPtr, &resultPtr); - if (result == TCL_OK) { - /* - * Store an integer based on the expression result. - */ - - if (resultPtr->typePtr == &tclIntType) { - *ptr = resultPtr->internalRep.longValue; - } else if (resultPtr->typePtr == &tclDoubleType) { - *ptr = (long) resultPtr->internalRep.doubleValue; - } else if (resultPtr->typePtr == &tclWideIntType) { -#ifndef TCL_WIDE_INT_IS_LONG - /* - * See Tcl_GetIntFromObj for conversion comments. - */ - Tcl_WideInt w = resultPtr->internalRep.wideValue; - if ((w >= -(Tcl_WideInt)(ULONG_MAX)) - && (w <= (Tcl_WideInt)(ULONG_MAX))) { - *ptr = Tcl_WideAsLong(w); - } else { - Tcl_SetResult(interp, - "integer value too large to represent as non-long integer", - TCL_STATIC); - result = TCL_ERROR; - } -#else - *ptr = resultPtr->internalRep.longValue; -#endif - } else { - Tcl_SetResult(interp, - "expression didn't have numeric value", TCL_STATIC); - result = TCL_ERROR; - } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ - } else { - /* - * Move the interpreter's object result to the string result, - * then reset the object result. - */ - - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); - } - Tcl_DecrRefCount(exprPtr); /* discard the expression object */ - } else { + if (*exprstring == '\0') { /* - * An empty string. Just set the result integer to 0. + * Legacy compatibility - return 0 for the zero-length string. */ - + *ptr = 0; + } else { + exprPtr = Tcl_NewStringObj(exprstring, -1); + Tcl_IncrRefCount(exprPtr); + result = Tcl_ExprLongObj(interp, exprPtr, ptr); + Tcl_DecrRefCount(exprPtr); + if (result != TCL_OK) { + (void) Tcl_GetStringResult(interp); + } } return result; } int -Tcl_ExprDouble(interp, string, ptr) - Tcl_Interp *interp; /* Context in which to evaluate the +Tcl_ExprDouble( + Tcl_Interp *interp, /* Context in which to evaluate the * expression. */ - CONST char *string; /* Expression to evaluate. */ - double *ptr; /* Where to store result. */ + const char *exprstring, /* Expression to evaluate. */ + double *ptr) /* Where to store result. */ { register Tcl_Obj *exprPtr; - Tcl_Obj *resultPtr; - int length = strlen(string); int result = TCL_OK; - if (length > 0) { - exprPtr = Tcl_NewStringObj(string, length); - Tcl_IncrRefCount(exprPtr); - result = Tcl_ExprObj(interp, exprPtr, &resultPtr); - if (result == TCL_OK) { - /* - * Store a double based on the expression result. - */ - - if (resultPtr->typePtr == &tclIntType) { - *ptr = (double) resultPtr->internalRep.longValue; - } else if (resultPtr->typePtr == &tclDoubleType) { - *ptr = resultPtr->internalRep.doubleValue; - } else if (resultPtr->typePtr == &tclWideIntType) { -#ifndef TCL_WIDE_INT_IS_LONG - /* - * See Tcl_GetIntFromObj for conversion comments. - */ - Tcl_WideInt w = resultPtr->internalRep.wideValue; - if ((w >= -(Tcl_WideInt)(ULONG_MAX)) - && (w <= (Tcl_WideInt)(ULONG_MAX))) { - *ptr = (double) Tcl_WideAsLong(w); - } else { - Tcl_SetResult(interp, - "integer value too large to represent as non-long integer", - TCL_STATIC); - result = TCL_ERROR; - } -#else - *ptr = (double) resultPtr->internalRep.longValue; -#endif - } else { - Tcl_SetResult(interp, - "expression didn't have numeric value", TCL_STATIC); - result = TCL_ERROR; - } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ - } else { - /* - * Move the interpreter's object result to the string result, - * then reset the object result. - */ - - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); - } - Tcl_DecrRefCount(exprPtr); /* discard the expression object */ - } else { + if (*exprstring == '\0') { /* - * An empty string. Just set the result double to 0.0. + * Legacy compatibility - return 0 for the zero-length string. */ - + *ptr = 0.0; + } else { + exprPtr = Tcl_NewStringObj(exprstring, -1); + Tcl_IncrRefCount(exprPtr); + result = Tcl_ExprDoubleObj(interp, exprPtr, ptr); + Tcl_DecrRefCount(exprPtr); + /* Discard the expression object. */ + if (result != TCL_OK) { + (void) Tcl_GetStringResult(interp); + } } return result; } int -Tcl_ExprBoolean(interp, string, ptr) - Tcl_Interp *interp; /* Context in which to evaluate the - * expression. */ - CONST char *string; /* Expression to evaluate. */ - int *ptr; /* Where to store 0/1 result. */ +Tcl_ExprBoolean( + Tcl_Interp *interp, /* Context in which to evaluate the + * expression. */ + const char *exprstring, /* Expression to evaluate. */ + int *ptr) /* Where to store 0/1 result. */ { - register Tcl_Obj *exprPtr; - Tcl_Obj *resultPtr; - int length = strlen(string); - int result = TCL_OK; + if (*exprstring == '\0') { + /* + * An empty string. Just set the result boolean to 0 (false). + */ - if (length > 0) { - exprPtr = Tcl_NewStringObj(string, length); - Tcl_IncrRefCount(exprPtr); - result = Tcl_ExprObj(interp, exprPtr, &resultPtr); - if (result == TCL_OK) { - /* - * Store a boolean based on the expression result. - */ + *ptr = 0; + return TCL_OK; + } else { + int result; + Tcl_Obj *exprPtr = Tcl_NewStringObj(exprstring, -1); - if (resultPtr->typePtr == &tclIntType) { - *ptr = (resultPtr->internalRep.longValue != 0); - } else if (resultPtr->typePtr == &tclDoubleType) { - *ptr = (resultPtr->internalRep.doubleValue != 0.0); - } else if (resultPtr->typePtr == &tclWideIntType) { -#ifndef TCL_WIDE_INT_IS_LONG - *ptr = (resultPtr->internalRep.wideValue != 0); -#else - *ptr = (resultPtr->internalRep.longValue != 0); -#endif - } else { - result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); - } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ - } + Tcl_IncrRefCount(exprPtr); + result = Tcl_ExprBooleanObj(interp, exprPtr, ptr); + Tcl_DecrRefCount(exprPtr); if (result != TCL_OK) { /* - * Move the interpreter's object result to the string result, - * then reset the object result. + * Move the interpreter's object result to the string result, then + * reset the object result. */ - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); + (void) Tcl_GetStringResult(interp); } - Tcl_DecrRefCount(exprPtr); /* discard the expression object */ - } else { - /* - * An empty string. Just set the result boolean to 0 (false). - */ - - *ptr = 0; + return result; } - return result; } /* @@ -5546,16 +5452,15 @@ Tcl_ExprBoolean(interp, string, ptr) * * Tcl_ExprLongObj, Tcl_ExprDoubleObj, Tcl_ExprBooleanObj -- * - * Procedures to evaluate an expression in an object and return its - * value in a particular form. + * Functions to evaluate an expression in an object and return its value + * in a particular form. * * Results: - * Each of the procedures below returns a standard Tcl result - * object. If an error occurs then an error message is left in the - * interpreter's result. Otherwise the value of the expression, in the - * appropriate form, is stored at *ptr. If the expression had a result - * that was incompatible with the desired form then an error is - * returned. + * Each of the functions below returns a standard Tcl result object. If + * an error occurs then an error message is left in the interpreter's + * result. Otherwise the value of the expression, in the appropriate + * form, is stored at *ptr. If the expression had a result that was + * incompatible with the desired form then an error is returned. * * Side effects: * None. @@ -5564,230 +5469,117 @@ Tcl_ExprBoolean(interp, string, ptr) */ int -Tcl_ExprLongObj(interp, objPtr, ptr) - Tcl_Interp *interp; /* Context in which to evaluate the - * expression. */ - register Tcl_Obj *objPtr; /* Expression to evaluate. */ - long *ptr; /* Where to store long result. */ +Tcl_ExprLongObj( + Tcl_Interp *interp, /* Context in which to evaluate the + * expression. */ + register Tcl_Obj *objPtr, /* Expression to evaluate. */ + long *ptr) /* Where to store long result. */ { Tcl_Obj *resultPtr; - int result; + int result, type; + double d; + ClientData internalPtr; result = Tcl_ExprObj(interp, objPtr, &resultPtr); - if (result == TCL_OK) { - if (resultPtr->typePtr == &tclIntType) { - *ptr = resultPtr->internalRep.longValue; - } else if (resultPtr->typePtr == &tclDoubleType) { - *ptr = (long) resultPtr->internalRep.doubleValue; - } else { - result = Tcl_GetLongFromObj(interp, resultPtr, ptr); - if (result != TCL_OK) { - return result; - } + if (result != TCL_OK) { + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, resultPtr, &internalPtr, &type) != TCL_OK){ + return TCL_ERROR; + } + + switch (type) { + case TCL_NUMBER_DOUBLE: { + mp_int big; + + d = *((const double *) internalPtr); + Tcl_DecrRefCount(resultPtr); + if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { + return TCL_ERROR; } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ + resultPtr = Tcl_NewBignumObj(&big); + /* FALLTHROUGH */ + } + case TCL_NUMBER_LONG: + case TCL_NUMBER_WIDE: + case TCL_NUMBER_BIG: + result = TclGetLongFromObj(interp, resultPtr, ptr); + break; + + case TCL_NUMBER_NAN: + Tcl_GetDoubleFromObj(interp, resultPtr, &d); + result = TCL_ERROR; } + + Tcl_DecrRefCount(resultPtr);/* Discard the result object. */ return result; } int -Tcl_ExprDoubleObj(interp, objPtr, ptr) - Tcl_Interp *interp; /* Context in which to evaluate the - * expression. */ - register Tcl_Obj *objPtr; /* Expression to evaluate. */ - double *ptr; /* Where to store double result. */ +Tcl_ExprDoubleObj( + Tcl_Interp *interp, /* Context in which to evaluate the + * expression. */ + register Tcl_Obj *objPtr, /* Expression to evaluate. */ + double *ptr) /* Where to store double result. */ { Tcl_Obj *resultPtr; - int result; + int result, type; + ClientData internalPtr; result = Tcl_ExprObj(interp, objPtr, &resultPtr); + if (result != TCL_OK) { + return TCL_ERROR; + } + + result = TclGetNumberFromObj(interp, resultPtr, &internalPtr, &type); if (result == TCL_OK) { - if (resultPtr->typePtr == &tclIntType) { - *ptr = (double) resultPtr->internalRep.longValue; - } else if (resultPtr->typePtr == &tclDoubleType) { - *ptr = resultPtr->internalRep.doubleValue; - } else { + switch (type) { + case TCL_NUMBER_NAN: +#ifndef ACCEPT_NAN + result = Tcl_GetDoubleFromObj(interp, resultPtr, ptr); + break; +#endif + case TCL_NUMBER_DOUBLE: + *ptr = *((const double *) internalPtr); + result = TCL_OK; + break; + default: result = Tcl_GetDoubleFromObj(interp, resultPtr, ptr); - if (result != TCL_OK) { - return result; - } } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ } + Tcl_DecrRefCount(resultPtr);/* Discard the result object. */ return result; } int -Tcl_ExprBooleanObj(interp, objPtr, ptr) - Tcl_Interp *interp; /* Context in which to evaluate the - * expression. */ - register Tcl_Obj *objPtr; /* Expression to evaluate. */ - int *ptr; /* Where to store 0/1 result. */ +Tcl_ExprBooleanObj( + Tcl_Interp *interp, /* Context in which to evaluate the + * expression. */ + register Tcl_Obj *objPtr, /* Expression to evaluate. */ + int *ptr) /* Where to store 0/1 result. */ { Tcl_Obj *resultPtr; int result; result = Tcl_ExprObj(interp, objPtr, &resultPtr); if (result == TCL_OK) { - if (resultPtr->typePtr == &tclIntType) { - *ptr = (resultPtr->internalRep.longValue != 0); - } else if (resultPtr->typePtr == &tclDoubleType) { - *ptr = (resultPtr->internalRep.doubleValue != 0.0); - } else { - result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); - } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * TclInvoke -- - * - * Invokes a Tcl command, given an argv/argc, from either the - * exposed or the hidden sets of commands in the given interpreter. - * NOTE: The command is invoked in the current stack frame of - * the interpreter, thus it can modify local variables. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * Whatever the command does. - * - *---------------------------------------------------------------------- - */ - -int -TclInvoke(interp, argc, argv, flags) - Tcl_Interp *interp; /* Where to invoke the command. */ - int argc; /* Count of args. */ - register CONST char **argv; /* The arg strings; argv[0] is the name of - * the command to invoke. */ - int flags; /* Combination of flags controlling the - * call: TCL_INVOKE_HIDDEN and - * TCL_INVOKE_NO_UNKNOWN. */ -{ - register Tcl_Obj *objPtr; - register int i; - int length, result; - - /* - * This procedure generates an objv array for object arguments that hold - * the argv strings. It starts out with stack-allocated space but uses - * dynamically-allocated storage if needed. - */ - -#define NUM_ARGS 20 - Tcl_Obj *(objStorage[NUM_ARGS]); - register Tcl_Obj **objv = objStorage; - - /* - * Create the object argument array "objv". Make sure objv is large - * enough to hold the objc arguments plus 1 extra for the zero - * end-of-objv word. - */ - - if ((argc + 1) > NUM_ARGS) { - objv = (Tcl_Obj **) - ckalloc((unsigned)(argc + 1) * sizeof(Tcl_Obj *)); - } - - for (i = 0; i < argc; i++) { - length = strlen(argv[i]); - objv[i] = Tcl_NewStringObj(argv[i], length); - Tcl_IncrRefCount(objv[i]); - } - objv[argc] = 0; - - /* - * Use TclObjInterpProc to actually invoke the command. - */ - - result = TclObjInvoke(interp, argc, objv, flags); - - /* - * Move the interpreter's object result to the string result, - * then reset the object result. - */ - - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); - - /* - * Decrement the ref counts on the objv elements since we are done - * with them. - */ - - for (i = 0; i < argc; i++) { - objPtr = objv[i]; - Tcl_DecrRefCount(objPtr); - } - - /* - * Free the objv array if malloc'ed storage was used. - */ - - if (objv != objStorage) { - ckfree((char *) objv); + result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); + Tcl_DecrRefCount(resultPtr); + /* Discard the result object. */ } return result; -#undef NUM_ARGS -} - -/* - *---------------------------------------------------------------------- - * - * TclGlobalInvoke -- - * - * Invokes a Tcl command, given an argv/argc, from either the - * exposed or hidden sets of commands in the given interpreter. - * NOTE: The command is invoked in the global stack frame of - * the interpreter, thus it cannot see any current state on - * the stack for that interpreter. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * Whatever the command does. - * - *---------------------------------------------------------------------- - */ - -int -TclGlobalInvoke(interp, argc, argv, flags) - Tcl_Interp *interp; /* Where to invoke the command. */ - int argc; /* Count of args. */ - register CONST char **argv; /* The arg strings; argv[0] is the name of - * the command to invoke. */ - int flags; /* Combination of flags controlling the - * call: TCL_INVOKE_HIDDEN and - * TCL_INVOKE_NO_UNKNOWN. */ -{ - register Interp *iPtr = (Interp *) interp; - int result; - CallFrame *savedVarFramePtr; - - savedVarFramePtr = iPtr->varFramePtr; - iPtr->varFramePtr = NULL; - result = TclInvoke(interp, argc, argv, flags); - iPtr->varFramePtr = savedVarFramePtr; - return result; } /* *---------------------------------------------------------------------- * - * TclObjInvokeGlobal -- + * TclObjInvokeNamespace -- * - * Object version: Invokes a Tcl command, given an objv/objc, from - * either the exposed or hidden set of commands in the given - * interpreter. + * Object version: Invokes a Tcl command, given an objv/objc, from either + * the exposed or hidden set of commands in the given interpreter. * NOTE: The command is invoked in the global stack frame of the - * interpreter, thus it cannot see any current state on the + * interpreter or namespace, thus it cannot see any current state on the * stack of that interpreter. * * Results: @@ -5800,25 +5592,33 @@ TclGlobalInvoke(interp, argc, argv, flags) */ int -TclObjInvokeGlobal(interp, objc, objv, flags) - Tcl_Interp *interp; /* Interpreter in which command is to be +TclObjInvokeNamespace( + Tcl_Interp *interp, /* Interpreter in which command is to be * invoked. */ - int objc; /* Count of arguments. */ - Tcl_Obj *CONST objv[]; /* Argument objects; objv[0] points to the + int objc, /* Count of arguments. */ + Tcl_Obj *const objv[], /* Argument objects; objv[0] points to the * name of the command to invoke. */ - int flags; /* Combination of flags controlling the - * call: TCL_INVOKE_HIDDEN, - * TCL_INVOKE_NO_UNKNOWN, or - * TCL_INVOKE_NO_TRACEBACK. */ + Tcl_Namespace *nsPtr, /* The namespace to use. */ + int flags) /* Combination of flags controlling the call: + * TCL_INVOKE_HIDDEN, TCL_INVOKE_NO_UNKNOWN, + * or TCL_INVOKE_NO_TRACEBACK. */ { - register Interp *iPtr = (Interp *) interp; int result; - CallFrame *savedVarFramePtr; + Tcl_CallFrame *framePtr; + + /* + * Make the specified namespace the current namespace and invoke the + * command. + */ + + result = TclPushStackFrame(interp, &framePtr, nsPtr, /*isProcFrame*/0); + if (result != TCL_OK) { + return TCL_ERROR; + } - savedVarFramePtr = iPtr->varFramePtr; - iPtr->varFramePtr = NULL; result = TclObjInvoke(interp, objc, objv, flags); - iPtr->varFramePtr = savedVarFramePtr; + + TclPopStackFrame(interp); return result; } @@ -5827,8 +5627,8 @@ TclObjInvokeGlobal(interp, objc, objv, flags) * * TclObjInvoke -- * - * Invokes a Tcl command, given an objv/objc, from either the - * exposed or the hidden sets of commands in the given interpreter. + * Invokes a Tcl command, given an objv/objc, from either the exposed or + * the hidden sets of commands in the given interpreter. * * Results: * A standard Tcl object result. @@ -5840,165 +5640,77 @@ TclObjInvokeGlobal(interp, objc, objv, flags) */ int -TclObjInvoke(interp, objc, objv, flags) - Tcl_Interp *interp; /* Interpreter in which command is to be +TclObjInvoke( + Tcl_Interp *interp, /* Interpreter in which command is to be * invoked. */ - int objc; /* Count of arguments. */ - Tcl_Obj *CONST objv[]; /* Argument objects; objv[0] points to the + int objc, /* Count of arguments. */ + Tcl_Obj *const objv[], /* Argument objects; objv[0] points to the * name of the command to invoke. */ - int flags; /* Combination of flags controlling the - * call: TCL_INVOKE_HIDDEN, - * TCL_INVOKE_NO_UNKNOWN, or - * TCL_INVOKE_NO_TRACEBACK. */ + int flags) /* Combination of flags controlling the call: + * TCL_INVOKE_HIDDEN, TCL_INVOKE_NO_UNKNOWN, + * or TCL_INVOKE_NO_TRACEBACK. */ { register Interp *iPtr = (Interp *) interp; Tcl_HashTable *hTblPtr; /* Table of hidden commands. */ char *cmdName; /* Name of the command from objv[0]. */ - register Tcl_HashEntry *hPtr; - Tcl_Command cmd; + Tcl_HashEntry *hPtr = NULL; Command *cmdPtr; - int localObjc; /* Used to invoke "unknown" if the */ - Tcl_Obj **localObjv = NULL; /* command is not found. */ - register int i; int result; - if (interp == (Tcl_Interp *) NULL) { - return TCL_ERROR; + if (interp == NULL) { + return TCL_ERROR; } - if ((objc < 1) || (objv == (Tcl_Obj **) NULL)) { - Tcl_AppendToObj(Tcl_GetObjResult(interp), - "illegal argument vector", -1); - return TCL_ERROR; + if ((objc < 1) || (objv == NULL)) { + Tcl_AppendResult(interp, "illegal argument vector", NULL); + return TCL_ERROR; } - cmdName = Tcl_GetString(objv[0]); - if (flags & TCL_INVOKE_HIDDEN) { - /* - * We never invoke "unknown" for hidden commands. - */ - - hPtr = NULL; - hTblPtr = ((Interp *) interp)->hiddenCmdTablePtr; - if (hTblPtr != NULL) { - hPtr = Tcl_FindHashEntry(hTblPtr, cmdName); - } - if (hPtr == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "invalid hidden command name \"", cmdName, "\"", - (char *) NULL); - return TCL_ERROR; - } - cmdPtr = (Command *) Tcl_GetHashValue(hPtr); - } else { - cmdPtr = NULL; - cmd = Tcl_FindCommand(interp, cmdName, - (Tcl_Namespace *) NULL, /*flags*/ TCL_GLOBAL_ONLY); - if (cmd != (Tcl_Command) NULL) { - cmdPtr = (Command *) cmd; - } - if (cmdPtr == NULL) { - if (!(flags & TCL_INVOKE_NO_UNKNOWN)) { - cmd = Tcl_FindCommand(interp, "unknown", - (Tcl_Namespace *) NULL, /*flags*/ TCL_GLOBAL_ONLY); - if (cmd != (Tcl_Command) NULL) { - cmdPtr = (Command *) cmd; - } - if (cmdPtr != NULL) { - localObjc = (objc + 1); - localObjv = (Tcl_Obj **) - ckalloc((unsigned) (sizeof(Tcl_Obj *) * localObjc)); - localObjv[0] = Tcl_NewStringObj("unknown", -1); - Tcl_IncrRefCount(localObjv[0]); - for (i = 0; i < objc; i++) { - localObjv[i+1] = objv[i]; - } - objc = localObjc; - objv = localObjv; - } - } - - /* - * Check again if we found the command. If not, "unknown" is - * not present and we cannot help, or the caller said not to - * call "unknown" (they specified TCL_INVOKE_NO_UNKNOWN). - */ - - if (cmdPtr == NULL) { - Tcl_ResetResult(interp); - Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), - "invalid command name \"", cmdName, "\"", - (char *) NULL); - return TCL_ERROR; - } - } - } - - /* - * Invoke the command procedure. First reset the interpreter's string - * and object results to their default empty values since they could - * have gotten changed by earlier invocations. + if ((flags & TCL_INVOKE_HIDDEN) == 0) { + Tcl_Panic("TclObjInvoke: called without TCL_INVOKE_HIDDEN"); + } + + if (TclInterpReady(interp) == TCL_ERROR) { + return TCL_ERROR; + } + + cmdName = TclGetString(objv[0]); + hTblPtr = iPtr->hiddenCmdTablePtr; + if (hTblPtr != NULL) { + hPtr = Tcl_FindHashEntry(hTblPtr, cmdName); + } + if (hPtr == NULL) { + Tcl_AppendResult(interp, "invalid hidden command name \"", + cmdName, "\"", NULL); + return TCL_ERROR; + } + cmdPtr = Tcl_GetHashValue(hPtr); + + /* + * Invoke the command function. */ - Tcl_ResetResult(interp); iPtr->cmdCount++; - result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv); + result = cmdPtr->objProc(cmdPtr->objClientData, interp, objc, objv); /* - * If an error occurred, record information about what was being - * executed when the error occurred. + * If an error occurred, record information about what was being executed + * when the error occurred. */ if ((result == TCL_ERROR) && ((flags & TCL_INVOKE_NO_TRACEBACK) == 0) && ((iPtr->flags & ERR_ALREADY_LOGGED) == 0)) { - Tcl_Obj *msg; - - if (!(iPtr->flags & ERR_IN_PROGRESS)) { - msg = Tcl_NewStringObj("\n while invoking\n\"", -1); - } else { - msg = Tcl_NewStringObj("\n invoked from within\n\"", -1); - } - Tcl_IncrRefCount(msg); - for (i = 0; i < objc; i++) { - CONST char *bytes; - int length; - - Tcl_AppendObjToObj(msg, objv[i]); - bytes = Tcl_GetStringFromObj(msg, &length); - if (length > 100) { - /* - * Back up truncation point so that we don't truncate - * in the middle of a multi-byte character. - */ - length = 100; - while ( (bytes[length] & 0xC0) == 0x80 ) { - length--; - } - Tcl_SetObjLength(msg, length); - Tcl_AppendToObj(msg, "...", -1); - break; - } - if (i != (objc - 1)) { - Tcl_AppendToObj(msg, " ", -1); - } - } - - Tcl_AppendToObj(msg, "\"", -1); - Tcl_AddObjErrorInfo(interp, Tcl_GetString(msg), -1); - Tcl_DecrRefCount(msg); + int length; + Tcl_Obj *command = Tcl_NewListObj(objc, objv); + const char *cmdString; + + Tcl_IncrRefCount(command); + cmdString = Tcl_GetStringFromObj(command, &length); + Tcl_LogCommandInfo(interp, cmdString, cmdString, length); + Tcl_DecrRefCount(command); iPtr->flags &= ~ERR_ALREADY_LOGGED; } - - /* - * Free any locally allocated storage used to call "unknown". - */ - - if (localObjv != (Tcl_Obj **) NULL) { - Tcl_DecrRefCount(localObjv[0]); - ckfree((char *) localObjv); - } return result; } @@ -6012,413 +5724,81 @@ TclObjInvoke(interp, objc, objv, flags) * * Results: * A standard Tcl result. If the result is TCL_OK, then the interp's - * result is set to the string value of the expression. If the result - * is TCL_ERROR, then the interp's result contains an error message. + * result is set to the string value of the expression. If the result is + * TCL_ERROR, then the interp's result contains an error message. * * Side effects: * A Tcl object is allocated to hold a copy of the expression string. - * This expression object is passed to Tcl_ExprObj and then - * deallocated. + * This expression object is passed to Tcl_ExprObj and then deallocated. * *--------------------------------------------------------------------------- */ int -Tcl_ExprString(interp, string) - Tcl_Interp *interp; /* Context in which to evaluate the +Tcl_ExprString( + Tcl_Interp *interp, /* Context in which to evaluate the * expression. */ - CONST char *string; /* Expression to evaluate. */ + const char *expr) /* Expression to evaluate. */ { - register Tcl_Obj *exprPtr; - Tcl_Obj *resultPtr; - int length = strlen(string); - char buf[TCL_DOUBLE_SPACE]; - int result = TCL_OK; - - if (length > 0) { - TclNewObj(exprPtr); - TclInitStringRep(exprPtr, string, length); - Tcl_IncrRefCount(exprPtr); + int code = TCL_OK; - result = Tcl_ExprObj(interp, exprPtr, &resultPtr); - if (result == TCL_OK) { - /* - * Set the interpreter's string result from the result object. - */ - - if (resultPtr->typePtr == &tclIntType) { - sprintf(buf, "%ld", resultPtr->internalRep.longValue); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - } else if (resultPtr->typePtr == &tclDoubleType) { - Tcl_PrintDouble((Tcl_Interp *) NULL, - resultPtr->internalRep.doubleValue, buf); - Tcl_SetResult(interp, buf, TCL_VOLATILE); - } else { - /* - * Set interpreter's string result from the result object. - */ - - Tcl_SetResult(interp, TclGetString(resultPtr), - TCL_VOLATILE); - } - Tcl_DecrRefCount(resultPtr); /* discard the result object */ - } else { - /* - * Move the interpreter's object result to the string result, - * then reset the object result. - */ - - Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), - TCL_VOLATILE); - } - Tcl_DecrRefCount(exprPtr); /* discard the expression object */ - } else { + if (expr[0] == '\0') { /* * An empty string. Just set the interpreter's result to 0. */ - - Tcl_SetResult(interp, "0", TCL_VOLATILE); - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_CreateObjTrace -- - * - * Arrange for a procedure to be called to trace command execution. - * - * Results: - * The return value is a token for the trace, which may be passed - * to Tcl_DeleteTrace to eliminate the trace. - * - * Side effects: - * From now on, proc will be called just before a command procedure - * is called to execute a Tcl command. Calls to proc will have the - * following form: - * - * void proc( ClientData clientData, - * Tcl_Interp* interp, - * int level, - * CONST char* command, - * Tcl_Command commandInfo, - * int objc, - * Tcl_Obj *CONST objv[] ); - * - * The 'clientData' and 'interp' arguments to 'proc' will be the - * same as the arguments to Tcl_CreateObjTrace. The 'level' - * argument gives the nesting depth of command interpretation within - * the interpreter. The 'command' argument is the ASCII text of - * the command being evaluated -- before any substitutions are - * performed. The 'commandInfo' argument gives a handle to the - * command procedure that will be evaluated. The 'objc' and 'objv' - * parameters give the parameter vector that will be passed to the - * command procedure. proc does not return a value. - * - * It is permissible for 'proc' to call Tcl_SetCommandTokenInfo - * to change the command procedure or client data for the command - * being evaluated, and these changes will take effect with the - * current evaluation. - * - * The 'level' argument specifies the maximum nesting level of calls - * to be traced. If the execution depth of the interpreter exceeds - * 'level', the trace callback is not executed. - * - * The 'flags' argument is either zero or the value, - * TCL_ALLOW_INLINE_COMPILATION. If the TCL_ALLOW_INLINE_COMPILATION - * flag is not present, the bytecode compiler will not generate inline - * code for Tcl's built-in commands. This behavior will have a significant - * impact on performance, but will ensure that all command evaluations are - * traced. If the TCL_ALLOW_INLINE_COMPILATION flag is present, the - * bytecode compiler will have its normal behavior of compiling in-line - * code for some of Tcl's built-in commands. In this case, the tracing - * will be imprecise -- in-line code will not be traced -- but run-time - * performance will be improved. The latter behavior is desired for - * many applications such as profiling of run time. - * - * When the trace is deleted, the 'delProc' procedure will be invoked, - * passing it the original client data. - * - *---------------------------------------------------------------------- - */ -Tcl_Trace -Tcl_CreateObjTrace( interp, level, flags, proc, clientData, delProc ) - Tcl_Interp* interp; /* Tcl interpreter */ - int level; /* Maximum nesting level */ - int flags; /* Flags, see above */ - Tcl_CmdObjTraceProc* proc; /* Trace callback */ - ClientData clientData; /* Client data for the callback */ - Tcl_CmdObjTraceDeleteProc* delProc; - /* Procedure to call when trace is deleted */ -{ - register Trace *tracePtr; - register Interp *iPtr = (Interp *) interp; - - /* Test if this trace allows inline compilation of commands */ - - if (!(flags & TCL_ALLOW_INLINE_COMPILATION)) { - if (iPtr->tracesForbiddingInline == 0) { - - /* - * When the first trace forbidding inline compilation is - * created, invalidate existing compiled code for this - * interpreter and arrange (by setting the - * DONT_COMPILE_CMDS_INLINE flag) that when compiling new - * code, no commands will be compiled inline (i.e., into - * an inline sequence of instructions). We do this because - * commands that were compiled inline will never result in - * a command trace being called. - */ - - iPtr->compileEpoch++; - iPtr->flags |= DONT_COMPILE_CMDS_INLINE; + Tcl_SetResult(interp, "0", TCL_VOLATILE); + } else { + Tcl_Obj *resultPtr, *exprObj = Tcl_NewStringObj(expr, -1); + + Tcl_IncrRefCount(exprObj); + code = Tcl_ExprObj(interp, exprObj, &resultPtr); + Tcl_DecrRefCount(exprObj); + if (code == TCL_OK) { + Tcl_SetObjResult(interp, resultPtr); + Tcl_DecrRefCount(resultPtr); } - iPtr->tracesForbiddingInline++; - } - - tracePtr = (Trace *) ckalloc(sizeof(Trace)); - tracePtr->level = level; - tracePtr->proc = proc; - tracePtr->clientData = clientData; - tracePtr->delProc = delProc; - tracePtr->nextPtr = iPtr->tracePtr; - tracePtr->flags = flags; - iPtr->tracePtr = tracePtr; - - return (Tcl_Trace) tracePtr; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_CreateTrace -- - * - * Arrange for a procedure to be called to trace command execution. - * - * Results: - * The return value is a token for the trace, which may be passed - * to Tcl_DeleteTrace to eliminate the trace. - * - * Side effects: - * From now on, proc will be called just before a command procedure - * is called to execute a Tcl command. Calls to proc will have the - * following form: - * - * void - * proc(clientData, interp, level, command, cmdProc, cmdClientData, - * argc, argv) - * ClientData clientData; - * Tcl_Interp *interp; - * int level; - * char *command; - * int (*cmdProc)(); - * ClientData cmdClientData; - * int argc; - * char **argv; - * { - * } - * - * The clientData and interp arguments to proc will be the same - * as the corresponding arguments to this procedure. Level gives - * the nesting level of command interpretation for this interpreter - * (0 corresponds to top level). Command gives the ASCII text of - * the raw command, cmdProc and cmdClientData give the procedure that - * will be called to process the command and the ClientData value it - * will receive, and argc and argv give the arguments to the - * command, after any argument parsing and substitution. Proc - * does not return a value. - * - *---------------------------------------------------------------------- - */ -Tcl_Trace -Tcl_CreateTrace(interp, level, proc, clientData) - Tcl_Interp *interp; /* Interpreter in which to create trace. */ - int level; /* Only call proc for commands at nesting - * level<=argument level (1=>top level). */ - Tcl_CmdTraceProc *proc; /* Procedure to call before executing each - * command. */ - ClientData clientData; /* Arbitrary value word to pass to proc. */ -{ - StringTraceData* data; - data = (StringTraceData*) ckalloc( sizeof( *data )); - data->clientData = clientData; - data->proc = proc; - return Tcl_CreateObjTrace( interp, level, 0, StringTraceProc, - (ClientData) data, StringTraceDeleteProc ); -} - -/* - *---------------------------------------------------------------------- - * - * StringTraceProc -- - * - * Invoke a string-based trace procedure from an object-based - * callback. - * - * Results: - * None. - * - * Side effects: - * Whatever the string-based trace procedure does. - * - *---------------------------------------------------------------------- - */ - -static int -StringTraceProc( clientData, interp, level, command, commandInfo, objc, objv ) - ClientData clientData; - Tcl_Interp* interp; - int level; - CONST char* command; - Tcl_Command commandInfo; - int objc; - Tcl_Obj *CONST *objv; -{ - StringTraceData* data = (StringTraceData*) clientData; - Command* cmdPtr = (Command*) commandInfo; - - CONST char** argv; /* Args to pass to string trace proc */ - - int i; + /* + * Force the string rep of the interp result. + */ - /* - * This is a bit messy because we have to emulate the old trace - * interface, which uses strings for everything. - */ - - argv = (CONST char **) ckalloc((unsigned) ( (objc + 1) - * sizeof(CONST char *) )); - for (i = 0; i < objc; i++) { - argv[i] = Tcl_GetString(objv[i]); + (void) Tcl_GetStringResult(interp); } - argv[objc] = 0; - - /* - * Invoke the command procedure. Note that we cast away const-ness - * on two parameters for compatibility with legacy code; the code - * MUST NOT modify either command or argv. - */ - - ( data->proc )( data->clientData, interp, level, - (char*) command, cmdPtr->proc, cmdPtr->clientData, - objc, argv ); - ckfree( (char*) argv ); - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * StringTraceDeleteProc -- - * - * Clean up memory when a string-based trace is deleted. - * - * Results: - * None. - * - * Side effects: - * Allocated memory is returned to the system. - * - *---------------------------------------------------------------------- - */ - -static void -StringTraceDeleteProc( clientData ) - ClientData clientData; -{ - ckfree( (char*) clientData ); + return code; } /* *---------------------------------------------------------------------- * - * Tcl_DeleteTrace -- + * Tcl_AppendObjToErrorInfo -- * - * Remove a trace. + * Add a Tcl_Obj value to the errorInfo field that describes the current + * error. * * Results: * None. * * Side effects: - * From now on there will be no more calls to the procedure given - * in trace. + * The value of the Tcl_obj is appended to the errorInfo field. If we are + * just starting to log an error, errorInfo is initialized from the error + * message in the interpreter's result. * *---------------------------------------------------------------------- */ void -Tcl_DeleteTrace(interp, trace) - Tcl_Interp *interp; /* Interpreter that contains trace. */ - Tcl_Trace trace; /* Token for trace (returned previously by - * Tcl_CreateTrace). */ +Tcl_AppendObjToErrorInfo( + Tcl_Interp *interp, /* Interpreter to which error information + * pertains. */ + Tcl_Obj *objPtr) /* Message to record. */ { - Interp *iPtr = (Interp *) interp; - Trace *prevPtr, *tracePtr = (Trace *) trace; - register Trace **tracePtr2 = &(iPtr->tracePtr); - ActiveInterpTrace *activePtr; - - /* - * Locate the trace entry in the interpreter's trace list, - * and remove it from the list. - */ - - prevPtr = NULL; - while ((*tracePtr2) != NULL && (*tracePtr2) != tracePtr) { - prevPtr = *tracePtr2; - tracePtr2 = &((*tracePtr2)->nextPtr); - } - if (*tracePtr2 == NULL) { - return; - } - (*tracePtr2) = (*tracePtr2)->nextPtr; - - /* - * The code below makes it possible to delete traces while traces - * are active: it makes sure that the deleted trace won't be - * processed by TclCheckInterpTraces. - */ - - for (activePtr = iPtr->activeInterpTracePtr; activePtr != NULL; - activePtr = activePtr->nextPtr) { - if (activePtr->nextTracePtr == tracePtr) { - if (activePtr->reverseScan) { - activePtr->nextTracePtr = prevPtr; - } else { - activePtr->nextTracePtr = tracePtr->nextPtr; - } - } - } - - /* - * If the trace forbids bytecode compilation, change the interpreter's - * state. If bytecode compilation is now permitted, flag the fact and - * advance the compilation epoch so that procs will be recompiled to - * take advantage of it. - */ - - if (!(tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION)) { - iPtr->tracesForbiddingInline--; - if (iPtr->tracesForbiddingInline == 0) { - iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE; - iPtr->compileEpoch++; - } - } + int length; + const char *message = TclGetStringFromObj(objPtr, &length); - /* - * Execute any delete callback. - */ - - if (tracePtr->delProc != NULL) { - (tracePtr->delProc)(tracePtr->clientData); - } - - /* Delete the trace object */ - - Tcl_EventuallyFree((char*)tracePtr, TCL_DYNAMIC); + Tcl_IncrRefCount(objPtr); + Tcl_AddObjErrorInfo(interp, message, length); + Tcl_DecrRefCount(objPtr); } /* @@ -6426,27 +5806,25 @@ Tcl_DeleteTrace(interp, trace) * * Tcl_AddErrorInfo -- * - * Add information to the "errorInfo" variable that describes the - * current error. + * Add information to the errorInfo field that describes the current + * error. * * Results: * None. * * Side effects: - * The contents of message are added to the "errorInfo" variable. - * If Tcl_Eval has been called since the current value of errorInfo - * was set, errorInfo is cleared before adding the new message. - * If we are just starting to log an error, errorInfo is initialized - * from the error message in the interpreter's result. + * The contents of message are appended to the errorInfo field. If we are + * just starting to log an error, errorInfo is initialized from the error + * message in the interpreter's result. * *---------------------------------------------------------------------- */ void -Tcl_AddErrorInfo(interp, message) - Tcl_Interp *interp; /* Interpreter to which error information +Tcl_AddErrorInfo( + Tcl_Interp *interp, /* Interpreter to which error information * pertains. */ - CONST char *message; /* Message to record. */ + const char *message) /* Message to record. */ { Tcl_AddObjErrorInfo(interp, message, -1); } @@ -6456,67 +5834,56 @@ Tcl_AddErrorInfo(interp, message) * * Tcl_AddObjErrorInfo -- * - * Add information to the "errorInfo" variable that describes the - * current error. This routine differs from Tcl_AddErrorInfo by - * taking a byte pointer and length. + * Add information to the errorInfo field that describes the current + * error. This routine differs from Tcl_AddErrorInfo by taking a byte + * pointer and length. * * Results: * None. * * Side effects: - * "length" bytes from "message" are added to the "errorInfo" variable. - * If "length" is negative, use bytes up to the first NULL byte. - * If Tcl_EvalObj has been called since the current value of errorInfo - * was set, errorInfo is cleared before adding the new message. - * If we are just starting to log an error, errorInfo is initialized - * from the error message in the interpreter's result. + * "length" bytes from "message" are appended to the errorInfo field. If + * "length" is negative, use bytes up to the first NULL byte. If we are + * just starting to log an error, errorInfo is initialized from the error + * message in the interpreter's result. * *---------------------------------------------------------------------- */ void -Tcl_AddObjErrorInfo(interp, message, length) - Tcl_Interp *interp; /* Interpreter to which error information +Tcl_AddObjErrorInfo( + Tcl_Interp *interp, /* Interpreter to which error information * pertains. */ - CONST char *message; /* Points to the first byte of an array of + const char *message, /* Points to the first byte of an array of * bytes of the message. */ - int length; /* The number of bytes in the message. - * If < 0, then append all bytes up to a - * NULL byte. */ + int length) /* The number of bytes in the message. If < 0, + * then append all bytes up to a NULL byte. */ { register Interp *iPtr = (Interp *) interp; - Tcl_Obj *objPtr; - + /* - * If we are just starting to log an error, errorInfo is initialized - * from the error message in the interpreter's result. + * If we are just starting to log an error, errorInfo is initialized from + * the error message in the interpreter's result. */ - if (!(iPtr->flags & ERR_IN_PROGRESS)) { /* just starting to log error */ - iPtr->flags |= ERR_IN_PROGRESS; + iPtr->flags |= ERR_LEGACY_COPY; + if (iPtr->errorInfo == NULL) { + if (iPtr->result[0] != 0) { + /* + * The interp's string result is set, apparently by some extension + * making a deprecated direct write to it. That extension may + * expect interp->result to continue to be set, so we'll take + * special pains to avoid clearing it, until we drop support for + * interp->result completely. + */ - if (iPtr->result[0] == 0) { - Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL, - iPtr->objResultPtr, TCL_GLOBAL_ONLY); - } else { /* use the string result */ - objPtr = Tcl_NewStringObj(interp->result, -1); - Tcl_IncrRefCount(objPtr); - Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL, - objPtr, TCL_GLOBAL_ONLY); - Tcl_DecrRefCount(objPtr); + iPtr->errorInfo = Tcl_NewStringObj(interp->result, -1); + } else { + iPtr->errorInfo = iPtr->objResultPtr; } - - /* - * If the errorCode variable wasn't set by the code that generated - * the error, set it to "NONE". - */ - - if (!(iPtr->flags & ERROR_CODE_SET)) { - objPtr = Tcl_NewStringObj("NONE", -1); - Tcl_IncrRefCount(objPtr); - Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorCode, NULL, - objPtr, TCL_GLOBAL_ONLY); - Tcl_DecrRefCount(objPtr); + Tcl_IncrRefCount(iPtr->errorInfo); + if (!iPtr->errorCode) { + Tcl_SetErrorCode(interp, "NONE", NULL); } } @@ -6525,11 +5892,12 @@ Tcl_AddObjErrorInfo(interp, message, length) */ if (length != 0) { - objPtr = Tcl_NewStringObj(message, length); - Tcl_IncrRefCount(objPtr); - Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL, - objPtr, (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE)); - Tcl_DecrRefCount(objPtr); /* free msg object appended above */ + if (Tcl_IsShared(iPtr->errorInfo)) { + Tcl_DecrRefCount(iPtr->errorInfo); + iPtr->errorInfo = Tcl_DuplicateObj(iPtr->errorInfo); + Tcl_IncrRefCount(iPtr->errorInfo); + } + Tcl_AppendToObj(iPtr->errorInfo, message, length); } } @@ -6538,12 +5906,12 @@ Tcl_AddObjErrorInfo(interp, message, length) * * Tcl_VarEvalVA -- * - * Given a variable number of string arguments, concatenate them - * all together and execute the result as a Tcl command. + * Given a variable number of string arguments, concatenate them all + * together and execute the result as a Tcl command. * * Results: - * A standard Tcl return result. An error message or other result may - * be left in the interp's result. + * A standard Tcl return result. An error message or other result may be + * left in the interp's result. * * Side effects: * Depends on what was done by the command. @@ -6552,19 +5920,18 @@ Tcl_AddObjErrorInfo(interp, message, length) */ int -Tcl_VarEvalVA (interp, argList) - Tcl_Interp *interp; /* Interpreter in which to evaluate command. */ - va_list argList; /* Variable argument list. */ +Tcl_VarEvalVA( + Tcl_Interp *interp, /* Interpreter in which to evaluate command. */ + va_list argList) /* Variable argument list. */ { Tcl_DString buf; char *string; int result; /* - * Copy the strings one after the other into a single larger - * string. Use stack-allocated space for small commands, but if - * the command gets too large than call ckalloc to create the - * space. + * Copy the strings one after the other into a single larger string. Use + * stack-allocated space for small commands, but if the command gets too + * large than call ckalloc to create the space. */ Tcl_DStringInit(&buf); @@ -6586,27 +5953,28 @@ Tcl_VarEvalVA (interp, argList) * * Tcl_VarEval -- * - * Given a variable number of string arguments, concatenate them - * all together and execute the result as a Tcl command. + * Given a variable number of string arguments, concatenate them all + * together and execute the result as a Tcl command. * * Results: - * A standard Tcl return result. An error message or other - * result may be left in interp->result. + * A standard Tcl return result. An error message or other result may be + * left in interp->result. * * Side effects: * Depends on what was done by the command. * *---------------------------------------------------------------------- */ - /* VARARGS2 */ /* ARGSUSED */ + /* ARGSUSED */ int -Tcl_VarEval TCL_VARARGS_DEF(Tcl_Interp *,arg1) +Tcl_VarEval( + Tcl_Interp *interp, + ...) { - Tcl_Interp *interp; va_list argList; int result; - interp = TCL_VARARGS_START(Tcl_Interp *,arg1,argList); + va_start(argList, interp); result = Tcl_VarEvalVA(interp, argList); va_end(argList); @@ -6614,36 +5982,35 @@ Tcl_VarEval TCL_VARARGS_DEF(Tcl_Interp *,arg1) } /* - *--------------------------------------------------------------------------- + *---------------------------------------------------------------------- * * Tcl_GlobalEval -- * * Evaluate a command at global level in an interpreter. * * Results: - * A standard Tcl result is returned, and the interp's result is - * modified accordingly. + * A standard Tcl result is returned, and the interp's result is modified + * accordingly. * * Side effects: - * The command string is executed in interp, and the execution - * is carried out in the variable context of global level (no - * procedures active), just as if an "uplevel #0" command were - * being executed. + * The command string is executed in interp, and the execution is carried + * out in the variable context of global level (no functions active), + * just as if an "uplevel #0" command were being executed. * - --------------------------------------------------------------------------- + *---------------------------------------------------------------------- */ int -Tcl_GlobalEval(interp, command) - Tcl_Interp *interp; /* Interpreter in which to evaluate command. */ - CONST char *command; /* Command to evaluate. */ +Tcl_GlobalEval( + Tcl_Interp *interp, /* Interpreter in which to evaluate command. */ + const char *command) /* Command to evaluate. */ { register Interp *iPtr = (Interp *) interp; int result; CallFrame *savedVarFramePtr; savedVarFramePtr = iPtr->varFramePtr; - iPtr->varFramePtr = NULL; + iPtr->varFramePtr = iPtr->rootFramePtr; result = Tcl_Eval(interp, command); iPtr->varFramePtr = savedVarFramePtr; return result; @@ -6654,8 +6021,8 @@ Tcl_GlobalEval(interp, command) * * Tcl_SetRecursionLimit -- * - * Set the maximum number of recursive calls that may be active - * for an interpreter at once. + * Set the maximum number of recursive calls that may be active for an + * interpreter at once. * * Results: * The return value is the old limit on nesting for interp. @@ -6667,10 +6034,10 @@ Tcl_GlobalEval(interp, command) */ int -Tcl_SetRecursionLimit(interp, depth) - Tcl_Interp *interp; /* Interpreter whose nesting limit - * is to be set. */ - int depth; /* New value for maximimum depth. */ +Tcl_SetRecursionLimit( + Tcl_Interp *interp, /* Interpreter whose nesting limit is to be + * set. */ + int depth) /* New value for maximimum depth. */ { Interp *iPtr = (Interp *) interp; int old; @@ -6687,39 +6054,36 @@ Tcl_SetRecursionLimit(interp, depth) * * Tcl_AllowExceptions -- * - * Sets a flag in an interpreter so that exceptions can occur - * in the next call to Tcl_Eval without them being turned into - * errors. + * Sets a flag in an interpreter so that exceptions can occur in the next + * call to Tcl_Eval without them being turned into errors. * * Results: * None. * * Side effects: - * The TCL_ALLOW_EXCEPTIONS flag gets set in the interpreter's - * evalFlags structure. See the reference documentation for - * more details. + * The TCL_ALLOW_EXCEPTIONS flag gets set in the interpreter's evalFlags + * structure. See the reference documentation for more details. * *---------------------------------------------------------------------- */ void -Tcl_AllowExceptions(interp) - Tcl_Interp *interp; /* Interpreter in which to set flag. */ +Tcl_AllowExceptions( + Tcl_Interp *interp) /* Interpreter in which to set flag. */ { Interp *iPtr = (Interp *) interp; iPtr->evalFlags |= TCL_ALLOW_EXCEPTIONS; } - /* *---------------------------------------------------------------------- * - * Tcl_GetVersion + * Tcl_GetVersion -- * - * Get the Tcl major, minor, and patchlevel version numbers and - * the release type. A patch is a release type TCL_FINAL_RELEASE - * with a patchLevel > 0. + * Get the Tcl major, minor, and patchlevel version numbers and the + * release type. A patch is a release type TCL_FINAL_RELEASE with a + * patchLevel > 0. * * Results: * None. @@ -6731,24 +6095,898 @@ Tcl_AllowExceptions(interp) */ void -Tcl_GetVersion(majorV, minorV, patchLevelV, type) - int *majorV; - int *minorV; - int *patchLevelV; - int *type; +Tcl_GetVersion( + int *majorV, + int *minorV, + int *patchLevelV, + int *type) { if (majorV != NULL) { - *majorV = TCL_MAJOR_VERSION; + *majorV = TCL_MAJOR_VERSION; } if (minorV != NULL) { - *minorV = TCL_MINOR_VERSION; + *minorV = TCL_MINOR_VERSION; } if (patchLevelV != NULL) { - *patchLevelV = TCL_RELEASE_SERIAL; + *patchLevelV = TCL_RELEASE_SERIAL; } if (type != NULL) { - *type = TCL_RELEASE_LEVEL; + *type = TCL_RELEASE_LEVEL; + } +} + +/* + *---------------------------------------------------------------------- + * + * Math Functions -- + * + * This page contains the functions that implement all of the built-in + * math functions for expressions. + * + * Results: + * Each function returns TCL_OK if it succeeds and pushes an Tcl object + * holding the result. If it fails it returns TCL_ERROR and leaves an + * error message in the interpreter's result. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +static int +ExprCeilFunc( + ClientData clientData, /* Ignored */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter list. */ +{ + int code; + double d; + mp_int big; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN + if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; + } +#endif + if (code != TCL_OK) { + return TCL_ERROR; + } + if (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK) { + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclCeil(&big))); + mp_clear(&big); + } else { + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(ceil(d))); + } + return TCL_OK; +} + +static int +ExprFloorFunc( + ClientData clientData, /* Ignored */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter list. */ +{ + int code; + double d; + mp_int big; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN + if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; + } +#endif + if (code != TCL_OK) { + return TCL_ERROR; + } + if (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK) { + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclFloor(&big))); + mp_clear(&big); + } else { + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(floor(d))); + } + return TCL_OK; +} + +static int +ExprIsqrtFunc( + ClientData clientData, /* Ignored */ + Tcl_Interp *interp, /* The interpreter in which to execute. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter list. */ +{ + ClientData ptr; + int type; + double d; + Tcl_WideInt w; + mp_int big; + int exact = 0; /* Flag == 1 if the argument can be + * represented in a double as an exact + * integer. */ + + /* + * Check syntax. + */ + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + /* + * Make sure that the arg is a number. + */ + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + + switch (type) { + case TCL_NUMBER_NAN: + Tcl_GetDoubleFromObj(interp, objv[1], &d); + return TCL_ERROR; + case TCL_NUMBER_DOUBLE: + d = *((const double *) ptr); + if (d < 0) { + goto negarg; + } +#ifdef IEEE_FLOATING_POINT + if (d <= MAX_EXACT) { + exact = 1; + } +#endif + if (!exact) { + if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { + return TCL_ERROR; + } + } + break; + case TCL_NUMBER_BIG: + if (Tcl_GetBignumFromObj(interp, objv[1], &big) != TCL_OK) { + return TCL_ERROR; + } + if (SIGN(&big) == MP_NEG) { + mp_clear(&big); + goto negarg; + } + break; + default: + if (Tcl_GetWideIntFromObj(interp, objv[1], &w) != TCL_OK) { + return TCL_ERROR; + } + if (w < 0) { + goto negarg; + } + d = (double) w; +#ifdef IEEE_FLOATING_POINT + if (d < MAX_EXACT) { + exact = 1; + } +#endif + if (!exact) { + Tcl_GetBignumFromObj(interp, objv[1], &big); + } + break; + } + + if (exact) { + Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt) sqrt(d))); + } else { + mp_int root; + + mp_init(&root); + mp_sqrt(&big, &root); + mp_clear(&big); + Tcl_SetObjResult(interp, Tcl_NewBignumObj(&root)); + } + + return TCL_OK; + + negarg: + Tcl_SetObjResult(interp, + Tcl_NewStringObj("square root of negative argument", -1)); + return TCL_ERROR; +} + +static int +ExprSqrtFunc( + ClientData clientData, /* Ignored */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter list. */ +{ + int code; + double d; + mp_int big; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN + if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; + } +#endif + if (code != TCL_OK) { + return TCL_ERROR; + } + if ((d >= 0.0) && TclIsInfinite(d) + && (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK)) { + mp_int root; + + mp_init(&root); + mp_sqrt(&big, &root); + mp_clear(&big); + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclBignumToDouble(&root))); + mp_clear(&root); + } else { + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sqrt(d))); + } + return TCL_OK; +} + +static int +ExprUnaryFunc( + ClientData clientData, /* Contains the address of a function that + * takes one double argument and returns a + * double result. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count */ + Tcl_Obj *const *objv) /* Actual parameter list */ +{ + int code; + double d; + double (*func)(double) = (double (*)(double)) clientData; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN + if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { + d = objv[1]->internalRep.doubleValue; + Tcl_ResetResult(interp); + code = TCL_OK; + } +#endif + if (code != TCL_OK) { + return TCL_ERROR; + } + errno = 0; + return CheckDoubleResult(interp, (*func)(d)); +} + +static int +CheckDoubleResult( + Tcl_Interp *interp, + double dResult) +{ +#ifndef ACCEPT_NAN + if (TclIsNaN(dResult)) { + TclExprFloatError(interp, dResult); + return TCL_ERROR; + } +#endif + if ((errno == ERANGE) && ((dResult == 0.0) || TclIsInfinite(dResult))) { + /* + * When ERANGE signals under/overflow, just accept 0.0 or +/-Inf + */ + } else if (errno != 0) { + /* + * Report other errno values as errors. + */ + + TclExprFloatError(interp, dResult); + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dResult)); + return TCL_OK; +} + +static int +ExprBinaryFunc( + ClientData clientData, /* Contains the address of a function that + * takes two double arguments and returns a + * double result. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Parameter vector. */ +{ + int code; + double d1, d2; + double (*func)(double, double) = (double (*)(double, double)) clientData; + + if (objc != 3) { + MathFuncWrongNumArgs(interp, 3, objc, objv); + return TCL_ERROR; } + code = Tcl_GetDoubleFromObj(interp, objv[1], &d1); +#ifdef ACCEPT_NAN + if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { + d1 = objv[1]->internalRep.doubleValue; + Tcl_ResetResult(interp); + code = TCL_OK; + } +#endif + if (code != TCL_OK) { + return TCL_ERROR; + } + code = Tcl_GetDoubleFromObj(interp, objv[2], &d2); +#ifdef ACCEPT_NAN + if ((code != TCL_OK) && (objv[2]->typePtr == &tclDoubleType)) { + d2 = objv[2]->internalRep.doubleValue; + Tcl_ResetResult(interp); + code = TCL_OK; + } +#endif + if (code != TCL_OK) { + return TCL_ERROR; + } + errno = 0; + return CheckDoubleResult(interp, (*func)(d1, d2)); +} + +static int +ExprAbsFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Parameter vector. */ +{ + ClientData ptr; + int type; + mp_int big; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + + if (type == TCL_NUMBER_LONG) { + long l = *((const long *) ptr); + + if (l > (long)0) { + goto unChanged; + } else if (l == (long)0) { + const char *string = objv[1]->bytes; + if (string) { + while (*string != '0') { + if (*string == '-') { + Tcl_SetObjResult(interp, Tcl_NewLongObj(0)); + return TCL_OK; + } + string++; + } + } + goto unChanged; + } else if (l == LONG_MIN) { + TclBNInitBignumFromLong(&big, l); + goto tooLarge; + } + Tcl_SetObjResult(interp, Tcl_NewLongObj(-l)); + return TCL_OK; + } + + if (type == TCL_NUMBER_DOUBLE) { + double d = *((const double *) ptr); + static const double poszero = 0.0; + + /* We need to distinguish here between positive 0.0 and + * negative -0.0, see Bug ID #2954959. + */ + if (d == -0.0) { + if (!memcmp(&d, &poszero, sizeof(double))) { + goto unChanged; + } + } else { + if (d > -0.0) { + goto unChanged; + } + } + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(-d)); + return TCL_OK; + } + +#ifndef NO_WIDE_TYPE + if (type == TCL_NUMBER_WIDE) { + Tcl_WideInt w = *((const Tcl_WideInt *) ptr); + + if (w >= (Tcl_WideInt)0) { + goto unChanged; + } + if (w == LLONG_MIN) { + TclBNInitBignumFromWideInt(&big, w); + goto tooLarge; + } + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(-w)); + return TCL_OK; + } +#endif + + if (type == TCL_NUMBER_BIG) { + /* TODO: const correctness ? */ + if (mp_cmp_d((mp_int *) ptr, 0) == MP_LT) { + Tcl_GetBignumFromObj(NULL, objv[1], &big); + tooLarge: + mp_neg(&big, &big); + Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); + } else { + unChanged: + Tcl_SetObjResult(interp, objv[1]); + } + return TCL_OK; + } + + if (type == TCL_NUMBER_NAN) { +#ifdef ACCEPT_NAN + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; +#else + double d; + Tcl_GetDoubleFromObj(interp, objv[1], &d); + return TCL_ERROR; +#endif + } + return TCL_OK; +} + +static int +ExprBoolFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + int value; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + if (Tcl_GetBooleanFromObj(interp, objv[1], &value) != TCL_OK) { + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value)); + return TCL_OK; +} + +static int +ExprDoubleFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + double dResult; + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + if (Tcl_GetDoubleFromObj(interp, objv[1], &dResult) != TCL_OK) { +#ifdef ACCEPT_NAN + if (objv[1]->typePtr == &tclDoubleType) { + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; + } +#endif + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dResult)); + return TCL_OK; +} + +static int +ExprEntierFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + double d; + int type; + ClientData ptr; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + + if (type == TCL_NUMBER_DOUBLE) { + d = *((const double *) ptr); + if ((d >= (double)LONG_MAX) || (d <= (double)LONG_MIN)) { + mp_int big; + + if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { + /* Infinity */ + return TCL_ERROR; + } + Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); + return TCL_OK; + } else { + long result = (long) d; + + Tcl_SetObjResult(interp, Tcl_NewLongObj(result)); + return TCL_OK; + } + } + + if (type != TCL_NUMBER_NAN) { + /* + * All integers are already of integer type. + */ + + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; + } + + /* + * Get the error message for NaN. + */ + + Tcl_GetDoubleFromObj(interp, objv[1], &d); + return TCL_ERROR; +} + +static int +ExprIntFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + long iResult; + Tcl_Obj *objPtr; + if (ExprEntierFunc(NULL, interp, objc, objv) != TCL_OK) { + return TCL_ERROR; + } + objPtr = Tcl_GetObjResult(interp); + if (TclGetLongFromObj(NULL, objPtr, &iResult) != TCL_OK) { + /* + * Truncate the bignum; keep only bits in long range. + */ + + mp_int big; + + Tcl_GetBignumFromObj(NULL, objPtr, &big); + mp_mod_2d(&big, (int) CHAR_BIT * sizeof(long), &big); + objPtr = Tcl_NewBignumObj(&big); + Tcl_IncrRefCount(objPtr); + TclGetLongFromObj(NULL, objPtr, &iResult); + Tcl_DecrRefCount(objPtr); + } + Tcl_SetObjResult(interp, Tcl_NewLongObj(iResult)); + return TCL_OK; +} + +static int +ExprWideFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + Tcl_WideInt wResult; + Tcl_Obj *objPtr; + if (ExprEntierFunc(NULL, interp, objc, objv) != TCL_OK) { + return TCL_ERROR; + } + objPtr = Tcl_GetObjResult(interp); + if (Tcl_GetWideIntFromObj(NULL, objPtr, &wResult) != TCL_OK) { + /* + * Truncate the bignum; keep only bits in wide int range. + */ + + mp_int big; + + Tcl_GetBignumFromObj(NULL, objPtr, &big); + mp_mod_2d(&big, (int) CHAR_BIT * sizeof(Tcl_WideInt), &big); + objPtr = Tcl_NewBignumObj(&big); + Tcl_IncrRefCount(objPtr); + Tcl_GetWideIntFromObj(NULL, objPtr, &wResult); + Tcl_DecrRefCount(objPtr); + } + Tcl_SetObjResult(interp, Tcl_NewWideIntObj(wResult)); + return TCL_OK; +} + +static int +ExprRandFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + Interp *iPtr = (Interp *) interp; + double dResult; + long tmp; /* Algorithm assumes at least 32 bits. Only + * long guarantees that. See below. */ + Tcl_Obj *oResult; + + if (objc != 1) { + MathFuncWrongNumArgs(interp, 1, objc, objv); + return TCL_ERROR; + } + + if (!(iPtr->flags & RAND_SEED_INITIALIZED)) { + iPtr->flags |= RAND_SEED_INITIALIZED; + + /* + * Take into consideration the thread this interp is running in order + * to insure different seeds in different threads (bug #416643) + */ + + iPtr->randSeed = TclpGetClicks() + (PTR2INT(Tcl_GetCurrentThread())<<12); + + /* + * Make sure 1 <= randSeed <= (2^31) - 2. See below. + */ + + iPtr->randSeed &= (unsigned long) 0x7fffffff; + if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { + iPtr->randSeed ^= 123459876; + } + } + + /* + * Generate the random number using the linear congruential generator + * defined by the following recurrence: + * seed = ( IA * seed ) mod IM + * where IA is 16807 and IM is (2^31) - 1. The recurrence maps a seed in + * the range [1, IM - 1] to a new seed in that same range. The recurrence + * maps IM to 0, and maps 0 back to 0, so those two values must not be + * allowed as initial values of seed. + * + * In order to avoid potential problems with integer overflow, the + * recurrence is implemented in terms of additional constants IQ and IR + * such that + * IM = IA*IQ + IR + * None of the operations in the implementation overflows a 32-bit signed + * integer, and the C type long is guaranteed to be at least 32 bits wide. + * + * For more details on how this algorithm works, refer to the following + * papers: + * + * S.K. Park & K.W. Miller, "Random number generators: good ones are hard + * to find," Comm ACM 31(10):1192-1201, Oct 1988 + * + * W.H. Press & S.A. Teukolsky, "Portable random number generators," + * Computers in Physics 6(5):522-524, Sep/Oct 1992. + */ + +#define RAND_IA 16807 +#define RAND_IM 2147483647 +#define RAND_IQ 127773 +#define RAND_IR 2836 +#define RAND_MASK 123459876 + + tmp = iPtr->randSeed/RAND_IQ; + iPtr->randSeed = RAND_IA*(iPtr->randSeed - tmp*RAND_IQ) - RAND_IR*tmp; + if (iPtr->randSeed < 0) { + iPtr->randSeed += RAND_IM; + } + + /* + * Since the recurrence keeps seed values in the range [1, RAND_IM - 1], + * dividing by RAND_IM yields a double in the range (0, 1). + */ + + dResult = iPtr->randSeed * (1.0/RAND_IM); + + /* + * Push a Tcl object with the result. + */ + + TclNewDoubleObj(oResult, dResult); + Tcl_SetObjResult(interp, oResult); + return TCL_OK; +} + +static int +ExprRoundFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Parameter vector. */ +{ + double d; + ClientData ptr; + int type; + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + return TCL_ERROR; + } + + if (type == TCL_NUMBER_DOUBLE) { + double fractPart, intPart; + long max = LONG_MAX, min = LONG_MIN; + + fractPart = modf(*((const double *) ptr), &intPart); + if (fractPart <= -0.5) { + min++; + } else if (fractPart >= 0.5) { + max--; + } + if ((intPart >= (double)max) || (intPart <= (double)min)) { + mp_int big; + + if (Tcl_InitBignumFromDouble(interp, intPart, &big) != TCL_OK) { + /* Infinity */ + return TCL_ERROR; + } + if (fractPart <= -0.5) { + mp_sub_d(&big, 1, &big); + } else if (fractPart >= 0.5) { + mp_add_d(&big, 1, &big); + } + Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); + return TCL_OK; + } else { + long result = (long)intPart; + + if (fractPart <= -0.5) { + result--; + } else if (fractPart >= 0.5) { + result++; + } + Tcl_SetObjResult(interp, Tcl_NewLongObj(result)); + return TCL_OK; + } + } + + if (type != TCL_NUMBER_NAN) { + /* + * All integers are already rounded + */ + + Tcl_SetObjResult(interp, objv[1]); + return TCL_OK; + } + + /* + * Get the error message for NaN. + */ + + Tcl_GetDoubleFromObj(interp, objv[1], &d); + return TCL_ERROR; +} + +static int +ExprSrandFunc( + ClientData clientData, /* Ignored. */ + Tcl_Interp *interp, /* The interpreter in which to execute the + * function. */ + int objc, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Parameter vector. */ +{ + Interp *iPtr = (Interp *) interp; + long i = 0; /* Initialized to avoid compiler warning. */ + + /* + * Convert argument and use it to reset the seed. + */ + + if (objc != 2) { + MathFuncWrongNumArgs(interp, 2, objc, objv); + return TCL_ERROR; + } + + if (TclGetLongFromObj(NULL, objv[1], &i) != TCL_OK) { + Tcl_Obj *objPtr; + mp_int big; + + if (Tcl_GetBignumFromObj(interp, objv[1], &big) != TCL_OK) { + /* TODO: more ::errorInfo here? or in caller? */ + return TCL_ERROR; + } + + mp_mod_2d(&big, (int) CHAR_BIT * sizeof(long), &big); + objPtr = Tcl_NewBignumObj(&big); + Tcl_IncrRefCount(objPtr); + TclGetLongFromObj(NULL, objPtr, &i); + Tcl_DecrRefCount(objPtr); + } + + /* + * Reset the seed. Make sure 1 <= randSeed <= 2^31 - 2. See comments in + * ExprRandFunc() for more details. + */ + + iPtr->flags |= RAND_SEED_INITIALIZED; + iPtr->randSeed = i; + iPtr->randSeed &= (unsigned long) 0x7fffffff; + if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { + iPtr->randSeed ^= 123459876; + } + + /* + * To avoid duplicating the random number generation code we simply clean + * up our state and call the real random number function. That function + * will always succeed. + */ + + return ExprRandFunc(clientData, interp, 1, objv); +} + +/* + *---------------------------------------------------------------------- + * + * MathFuncWrongNumArgs -- + * + * Generate an error message when a math function presents the wrong + * number of arguments. + * + * Results: + * None. + * + * Side effects: + * An error message is stored in the interpreter result. + * + *---------------------------------------------------------------------- + */ + +static void +MathFuncWrongNumArgs( + Tcl_Interp *interp, /* Tcl interpreter */ + int expected, /* Formal parameter count. */ + int found, /* Actual parameter count. */ + Tcl_Obj *const *objv) /* Actual parameter vector. */ +{ + const char *name = Tcl_GetString(objv[0]); + const char *tail = name + strlen(name); + + while (tail > name+1) { + --tail; + if (*tail == ':' && tail[-1] == ':') { + name = tail+1; + break; + } + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "too %s arguments for math function \"%s\"", + (found < expected ? "few" : "many"), name)); } #ifdef USE_DTRACE @@ -6773,7 +7011,7 @@ DTraceObjCmd( ClientData dummy, /* Not used. */ Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ - Tcl_Obj *CONST objv[]) /* Argument objects. */ + Tcl_Obj *const objv[]) /* Argument objects. */ { if (TCL_DTRACE_TCL_PROBE_ENABLED()) { char *a[10]; @@ -6787,6 +7025,60 @@ DTraceObjCmd( } return TCL_OK; } + +/* + *---------------------------------------------------------------------- + * + * TclDTraceInfo -- + * + * Extract information from a TIP280 dict for use by DTrace probes. + * + * Results: + * None. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +void +TclDTraceInfo( + Tcl_Obj *info, + char **args, + int *argsi) +{ + static Tcl_Obj *keys[7] = { NULL }; + Tcl_Obj **k = keys, *val; + int i; + + if (!*k) { + TclNewLiteralStringObj(keys[0], "cmd"); + TclNewLiteralStringObj(keys[1], "type"); + TclNewLiteralStringObj(keys[2], "proc"); + TclNewLiteralStringObj(keys[3], "file"); + TclNewLiteralStringObj(keys[4], "lambda"); + TclNewLiteralStringObj(keys[5], "line"); + TclNewLiteralStringObj(keys[6], "level"); + } + for (i = 0; i < 4; i++) { + Tcl_DictObjGet(NULL, info, *k++, &val); + args[i] = val ? TclGetString(val) : NULL; + } + if (!args[2]) { + Tcl_DictObjGet(NULL, info, *k, &val); + args[2] = val ? TclGetString(val) : NULL; + } + k++; + for (i = 0; i < 2; i++) { + Tcl_DictObjGet(NULL, info, *k++, &val); + if (val) { + TclGetIntFromObj(NULL, val, &(argsi[i])); + } else { + argsi[i] = 0; + } + } +} TCL_DTRACE_DEBUG_LOG() |