diff options
Diffstat (limited to 'generic/tclBasic.c')
| -rw-r--r-- | generic/tclBasic.c | 6869 |
1 files changed, 1755 insertions, 5114 deletions
diff --git a/generic/tclBasic.c b/generic/tclBasic.c index 02607a4..44cf543 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -5,102 +5,22 @@ * including interpreter creation and deletion, command creation and * deletion, and command/script execution. * - * Copyright © 1987-1994 The Regents of the University of California. - * Copyright © 1994-1997 Sun Microsystems, Inc. - * Copyright © 1998-1999 Scriptics Corporation. - * Copyright © 2001, 2002 Kevin B. Kenny. All rights reserved. - * Copyright © 2007 Daniel A. Steffen <das@users.sourceforge.net> - * Copyright © 2006-2008 Joe Mistachkin. All rights reserved. - * Copyright © 2008 Miguel Sofer <msofer@users.sourceforge.net> + * Copyright (c) 1987-1994 The Regents of the University of California. + * Copyright (c) 1994-1997 Sun Microsystems, Inc. + * Copyright (c) 1998-1999 by Scriptics Corporation. + * 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. */ #include "tclInt.h" -#include "tclOOInt.h" #include "tclCompile.h" -#include "tclTomMath.h" -#include <math.h> -#include <assert.h> - -/* - * TCL_FPCLASSIFY_MODE: - * 0 - fpclassify - * 1 - _fpclass - * 2 - simulate - * 3 - __builtin_fpclassify - */ - -#ifndef TCL_FPCLASSIFY_MODE -#if defined(__MINGW32__) && defined(_X86_) /* mingw 32-bit */ -/* - * MINGW x86 (tested up to gcc 8.1) seems to have a bug in fpclassify, - * [fpclassify 1e-314], x86 => normal, x64 => subnormal, so switch to using a - * version using a compiler built-in. - */ -#define TCL_FPCLASSIFY_MODE 1 -#elif defined(fpclassify) /* fpclassify */ -/* - * This is the C99 standard. - */ #include <float.h> -#define TCL_FPCLASSIFY_MODE 0 -#elif defined(_FPCLASS_NN) /* _fpclass */ -/* - * This case handles newer MSVC on Windows, which doesn't have the standard - * operation but does have something that can tell us the same thing. - */ -#define TCL_FPCLASSIFY_MODE 1 -#else /* !fpclassify && !_fpclass (older MSVC), simulate */ -/* - * Older MSVC on Windows. So broken that we just have to do it our way. This - * assumes that we're on x86 (or at least a system with classic little-endian - * double layout and a 32-bit 'int' type). - */ -#define TCL_FPCLASSIFY_MODE 2 -#endif /* !fpclassify */ -/* actually there is no fallback to builtin fpclassify */ -#endif /* !TCL_FPCLASSIFY_MODE */ - - -/* - * Bug 7371b6270b: to check C call stack depth, prefer an approach which is - * compatible with AddressSanitizer (ASan) use-after-return detection. - */ - -#if defined(_MSC_VER) && defined(HAVE_INTRIN_H) -#include <intrin.h> /* for _AddressOfReturnAddress() */ -#endif - -/* - * As suggested by - * https://clang.llvm.org/docs/LanguageExtensions.html#has-builtin - */ -#ifndef __has_builtin -#define __has_builtin(x) 0 /* for non-clang compilers */ -#endif - -void * -TclGetCStackPtr(void) -{ -#if defined( __GNUC__ ) || __has_builtin(__builtin_frame_address) - return __builtin_frame_address(0); -#elif defined(_MSC_VER) && defined(HAVE_INTRIN_H) - return _AddressOfReturnAddress(); -#else - ptrdiff_t unused = 0; - /* - * LLVM recommends using volatile: - * https://github.com/llvm/llvm-project/blob/llvmorg-10.0.0-rc1/clang/lib/Basic/Stack.cpp#L31 - */ - ptrdiff_t *volatile stackLevel = &unused; - return (void *)stackLevel; -#endif -} - -#define INTERP_STACK_INITIAL_SIZE 2000 -#define CORO_STACK_INITIAL_SIZE 200 +#include <limits.h> +#include <math.h> +#include "tommath.h" /* * Determine whether we're using IEEE floating point @@ -121,159 +41,63 @@ typedef struct OldMathFuncData { Tcl_MathProc *proc; /* Handler function */ int numArgs; /* Number of args expected */ Tcl_ValueType *argTypes; /* Types of the args */ - void *clientData; /* Client data for the handler function */ + ClientData clientData; /* Client data for the handler function */ } OldMathFuncData; /* - * This is the script cancellation struct and hash table. The hash table is - * used to keep track of the information necessary to process script - * cancellation requests, including the original interp, asynchronous handler - * tokens (created by Tcl_AsyncCreate), and the clientData and flags arguments - * passed to Tcl_CancelEval on a per-interp basis. The cancelLock mutex is - * used for protecting calls to Tcl_CancelEval as well as protecting access to - * the hash table below. - */ - -typedef struct { - Tcl_Interp *interp; /* Interp this struct belongs to. */ - Tcl_AsyncHandler async; /* Async handler token for script - * cancellation. */ - char *result; /* The script cancellation result or NULL for - * a default result. */ - Tcl_Size length; /* Length of the above error message. */ - void *clientData; /* Not used. */ - int flags; /* Additional flags */ -} CancelInfo; -static Tcl_HashTable cancelTable; -static int cancelTableInitialized = 0; /* 0 means not yet initialized. */ -TCL_DECLARE_MUTEX(cancelLock); - -/* - * Table used to map command implementation functions to a human-readable type - * name, for [info type]. The keys in the table are function addresses, and - * the values in the table are static char* containing strings in Tcl's - * internal encoding (almost UTF-8). - */ - -static Tcl_HashTable commandTypeTable; -static int commandTypeInit = 0; -TCL_DECLARE_MUTEX(commandTypeLock); - -/* - * Declarations for managing contexts for non-recursive coroutines. Contexts - * are used to save the evaluation state between NR calls to each coro. - */ - -#define SAVE_CONTEXT(context) \ - (context).framePtr = iPtr->framePtr; \ - (context).varFramePtr = iPtr->varFramePtr; \ - (context).cmdFramePtr = iPtr->cmdFramePtr; \ - (context).lineLABCPtr = iPtr->lineLABCPtr - -#define RESTORE_CONTEXT(context) \ - iPtr->framePtr = (context).framePtr; \ - iPtr->varFramePtr = (context).varFramePtr; \ - iPtr->cmdFramePtr = (context).cmdFramePtr; \ - iPtr->lineLABCPtr = (context).lineLABCPtr - -/* * Static functions in this file: */ -static Tcl_ObjCmdProc BadEnsembleSubcommand; -static char * CallCommandTraces(Interp *iPtr, Command *cmdPtr, - const char *oldName, const char *newName, - int flags); -static int CancelEvalProc(void *clientData, - Tcl_Interp *interp, int code); -static int CheckDoubleResult(Tcl_Interp *interp, double dResult); -static void DeleteCoroutine(void *clientData); -static Tcl_FreeProc DeleteInterpProc; -static void DeleteOpCmdClientData(void *clientData); +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 Tcl_ObjCmdProc DTraceObjCmd; -static Tcl_NRPostProc DTraceCmdReturn; -#else -# define DTraceCmdReturn NULL -#endif /* USE_DTRACE */ -static Tcl_ObjCmdProc ExprAbsFunc; -static Tcl_ObjCmdProc ExprBinaryFunc; -static Tcl_ObjCmdProc ExprBoolFunc; -static Tcl_ObjCmdProc ExprCeilFunc; -static Tcl_ObjCmdProc ExprDoubleFunc; -static Tcl_ObjCmdProc ExprFloorFunc; -static Tcl_ObjCmdProc ExprIntFunc; -static Tcl_ObjCmdProc ExprIsqrtFunc; -static Tcl_ObjCmdProc ExprIsFiniteFunc; -static Tcl_ObjCmdProc ExprIsInfinityFunc; -static Tcl_ObjCmdProc ExprIsNaNFunc; -static Tcl_ObjCmdProc ExprIsNormalFunc; -static Tcl_ObjCmdProc ExprIsSubnormalFunc; -static Tcl_ObjCmdProc ExprIsUnorderedFunc; -static Tcl_ObjCmdProc ExprMaxFunc; -static Tcl_ObjCmdProc ExprMinFunc; -static Tcl_ObjCmdProc ExprRandFunc; -static Tcl_ObjCmdProc ExprRoundFunc; -static Tcl_ObjCmdProc ExprSqrtFunc; -static Tcl_ObjCmdProc ExprSrandFunc; -static Tcl_ObjCmdProc ExprUnaryFunc; -static Tcl_ObjCmdProc ExprWideFunc; -static Tcl_ObjCmdProc FloatClassifyObjCmd; -static void MathFuncWrongNumArgs(Tcl_Interp *interp, int expected, - int actual, Tcl_Obj *const *objv); -static Tcl_NRPostProc NRCoroutineCallerCallback; -static Tcl_NRPostProc NRCoroutineExitCallback; -static Tcl_NRPostProc NRCommand; - -#if !defined(TCL_NO_DEPRECATED) -static Tcl_ObjCmdProc OldMathFuncProc; -static void OldMathFuncDeleteProc(void *clientData); -#endif /* !defined(TCL_NO_DEPRECATED) */ -static void ProcessUnexpectedResult(Tcl_Interp *interp, - int returnCode); -static int RewindCoroutine(CoroutineData *corPtr, int result); -static void TEOV_SwitchVarFrame(Tcl_Interp *interp); -static void TEOV_PushExceptionHandlers(Tcl_Interp *interp, - Tcl_Size objc, Tcl_Obj *const objv[], int flags); -static inline Command * TEOV_LookupCmdFromObj(Tcl_Interp *interp, - Tcl_Obj *namePtr, Namespace *lookupNsPtr); -static int TEOV_NotFound(Tcl_Interp *interp, Tcl_Size objc, - Tcl_Obj *const objv[], Namespace *lookupNsPtr); -static int TEOV_RunEnterTraces(Tcl_Interp *interp, - Command **cmdPtrPtr, Tcl_Obj *commandPtr, Tcl_Size objc, - Tcl_Obj *const objv[]); -static Tcl_NRPostProc RewindCoroutineCallback; -static Tcl_NRPostProc TEOEx_ByteCodeCallback; -static Tcl_NRPostProc TEOEx_ListCallback; -static Tcl_NRPostProc TEOV_Error; -static Tcl_NRPostProc TEOV_Exception; -static Tcl_NRPostProc TEOV_NotFoundCallback; -static Tcl_NRPostProc TEOV_RestoreVarFrame; -static Tcl_NRPostProc TEOV_RunLeaveTraces; -static Tcl_NRPostProc EvalObjvCore; -static Tcl_NRPostProc Dispatch; - -static Tcl_ObjCmdProc NRInjectObjCmd; -static Tcl_NRPostProc NRPostInvoke; -static Tcl_ObjCmdProc CoroTypeObjCmd; -static Tcl_ObjCmdProc TclNRCoroInjectObjCmd; -static Tcl_ObjCmdProc TclNRCoroProbeObjCmd; -static Tcl_NRPostProc InjectHandler; -static Tcl_NRPostProc InjectHandlerPostCall; - -MODULE_SCOPE const TclStubs tclStubs; - -/* - * Magical counts for the number of arguments accepted by a coroutine command - * after particular kinds of [yield]. - */ +static int DTraceObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, + Tcl_Obj *const objv[]); +#endif -#define CORO_ACTIVATE_YIELD NULL -#define CORO_ACTIVATE_YIELDM INT2PTR(1) +extern TclStubs tclStubs; -#define COROUTINE_ARGUMENTS_SINGLE_OPTIONAL (-1) -#define COROUTINE_ARGUMENTS_ARBITRARY (-2) - /* * The following structure define the commands in the Tcl core. */ @@ -282,35 +106,11 @@ 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. */ - Tcl_ObjCmdProc *nreProc; /* NR-based function for command */ - int flags; /* Various flag bits, as defined below. */ + int isSafe; /* If non-zero, command will be present in + * safe interpreter. Otherwise it will be + * hidden. */ } CmdInfo; -#define CMD_IS_SAFE 1 /* Whether this command is part of the set of - * commands present by default in a safe - * interpreter. */ -/* CMD_COMPILES_EXPANDED - Whether the compiler for this command can handle - * expansion for itself rather than needing the generic layer to take care of - * it for it. Defined in tclInt.h. */ - -/* - * The following struct states that the command it talks about (a subcommand - * of one of Tcl's built-in ensembles) is unsafe and must be hidden when an - * interpreter is made safe. (TclHideUnsafeCommands accesses an array of these - * structs.) Alas, we can't sensibly just store the information directly in - * the commands. - */ - -typedef struct { - const char *ensembleNsName; /* The ensemble's name within ::tcl. NULL for - * the end of the list of commands to hide. */ - const char *commandName; /* The name of the command within the - * ensemble. If this is NULL, we want to also - * make the overall command be hidden, an ugly - * hack because it is expected by security - * policies in the wild. */ -} UnsafeEnsembleInfo; - /* * The built-in commands, and the functions that implement them: */ @@ -320,166 +120,93 @@ static const CmdInfo builtInCmds[] = { * Commands in the generic core. */ - {"append", Tcl_AppendObjCmd, TclCompileAppendCmd, NULL, CMD_IS_SAFE}, - {"apply", Tcl_ApplyObjCmd, NULL, TclNRApplyObjCmd, CMD_IS_SAFE}, - {"break", Tcl_BreakObjCmd, TclCompileBreakCmd, NULL, CMD_IS_SAFE}, -#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 - {"case", Tcl_CaseObjCmd, NULL, NULL, CMD_IS_SAFE}, + {"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, TclNRCatchObjCmd, CMD_IS_SAFE}, - {"concat", Tcl_ConcatObjCmd, TclCompileConcatCmd, NULL, CMD_IS_SAFE}, - {"continue", Tcl_ContinueObjCmd, TclCompileContinueCmd, NULL, CMD_IS_SAFE}, - {"coroinject", NULL, NULL, TclNRCoroInjectObjCmd, CMD_IS_SAFE}, - {"coroprobe", NULL, NULL, TclNRCoroProbeObjCmd, CMD_IS_SAFE}, - {"coroutine", NULL, NULL, TclNRCoroutineObjCmd, CMD_IS_SAFE}, - {"error", Tcl_ErrorObjCmd, TclCompileErrorCmd, NULL, CMD_IS_SAFE}, - {"eval", Tcl_EvalObjCmd, NULL, TclNREvalObjCmd, CMD_IS_SAFE}, - {"expr", Tcl_ExprObjCmd, TclCompileExprCmd, TclNRExprObjCmd, CMD_IS_SAFE}, - {"for", Tcl_ForObjCmd, TclCompileForCmd, TclNRForObjCmd, CMD_IS_SAFE}, - {"foreach", Tcl_ForeachObjCmd, TclCompileForeachCmd, TclNRForeachCmd, CMD_IS_SAFE}, - {"format", Tcl_FormatObjCmd, TclCompileFormatCmd, NULL, CMD_IS_SAFE}, - {"fpclassify", FloatClassifyObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"global", Tcl_GlobalObjCmd, TclCompileGlobalCmd, NULL, CMD_IS_SAFE}, - {"if", Tcl_IfObjCmd, TclCompileIfCmd, TclNRIfObjCmd, CMD_IS_SAFE}, - {"incr", Tcl_IncrObjCmd, TclCompileIncrCmd, NULL, CMD_IS_SAFE}, - {"join", Tcl_JoinObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lappend", Tcl_LappendObjCmd, TclCompileLappendCmd, NULL, CMD_IS_SAFE}, - {"lassign", Tcl_LassignObjCmd, TclCompileLassignCmd, NULL, CMD_IS_SAFE}, - {"ledit", Tcl_LeditObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lindex", Tcl_LindexObjCmd, TclCompileLindexCmd, NULL, CMD_IS_SAFE}, - {"linsert", Tcl_LinsertObjCmd, TclCompileLinsertCmd, NULL, CMD_IS_SAFE}, - {"list", Tcl_ListObjCmd, TclCompileListCmd, NULL, CMD_IS_SAFE|CMD_COMPILES_EXPANDED}, - {"llength", Tcl_LlengthObjCmd, TclCompileLlengthCmd, NULL, CMD_IS_SAFE}, - {"lmap", Tcl_LmapObjCmd, TclCompileLmapCmd, TclNRLmapCmd, CMD_IS_SAFE}, - {"lpop", Tcl_LpopObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lrange", Tcl_LrangeObjCmd, TclCompileLrangeCmd, NULL, CMD_IS_SAFE}, - {"lremove", Tcl_LremoveObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lrepeat", Tcl_LrepeatObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lreplace", Tcl_LreplaceObjCmd, TclCompileLreplaceCmd, NULL, CMD_IS_SAFE}, - {"lreverse", Tcl_LreverseObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lsearch", Tcl_LsearchObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lseq", Tcl_LseqObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"lset", Tcl_LsetObjCmd, TclCompileLsetCmd, NULL, CMD_IS_SAFE}, - {"lsort", Tcl_LsortObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"package", Tcl_PackageObjCmd, NULL, TclNRPackageObjCmd, CMD_IS_SAFE}, - {"proc", Tcl_ProcObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"regexp", Tcl_RegexpObjCmd, TclCompileRegexpCmd, NULL, CMD_IS_SAFE}, - {"regsub", Tcl_RegsubObjCmd, TclCompileRegsubCmd, NULL, CMD_IS_SAFE}, - {"rename", Tcl_RenameObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"return", Tcl_ReturnObjCmd, TclCompileReturnCmd, NULL, CMD_IS_SAFE}, - {"scan", Tcl_ScanObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"set", Tcl_SetObjCmd, TclCompileSetCmd, NULL, CMD_IS_SAFE}, - {"split", Tcl_SplitObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"subst", Tcl_SubstObjCmd, TclCompileSubstCmd, TclNRSubstObjCmd, CMD_IS_SAFE}, - {"switch", Tcl_SwitchObjCmd, TclCompileSwitchCmd, TclNRSwitchObjCmd, CMD_IS_SAFE}, - {"tailcall", NULL, TclCompileTailcallCmd, TclNRTailcallObjCmd, CMD_IS_SAFE}, - {"throw", Tcl_ThrowObjCmd, TclCompileThrowCmd, NULL, CMD_IS_SAFE}, - {"trace", Tcl_TraceObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"try", Tcl_TryObjCmd, TclCompileTryCmd, TclNRTryObjCmd, CMD_IS_SAFE}, - {"unset", Tcl_UnsetObjCmd, TclCompileUnsetCmd, NULL, CMD_IS_SAFE}, - {"uplevel", Tcl_UplevelObjCmd, NULL, TclNRUplevelObjCmd, CMD_IS_SAFE}, - {"upvar", Tcl_UpvarObjCmd, TclCompileUpvarCmd, NULL, CMD_IS_SAFE}, - {"variable", Tcl_VariableObjCmd, TclCompileVariableCmd, NULL, CMD_IS_SAFE}, - {"while", Tcl_WhileObjCmd, TclCompileWhileCmd, TclNRWhileObjCmd, CMD_IS_SAFE}, - {"yield", NULL, TclCompileYieldCmd, TclNRYieldObjCmd, CMD_IS_SAFE}, - {"yieldto", NULL, TclCompileYieldToCmd, TclNRYieldToObjCmd, CMD_IS_SAFE}, + {"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, NULL, CMD_IS_SAFE}, - {"cd", Tcl_CdObjCmd, NULL, NULL, 0}, - {"close", Tcl_CloseObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"eof", Tcl_EofObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"exec", Tcl_ExecObjCmd, NULL, NULL, 0}, - {"exit", Tcl_ExitObjCmd, NULL, NULL, 0}, - {"fblocked", Tcl_FblockedObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"fconfigure", Tcl_FconfigureObjCmd, NULL, NULL, 0}, - {"fcopy", Tcl_FcopyObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"fileevent", Tcl_FileEventObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"flush", Tcl_FlushObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"gets", Tcl_GetsObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"glob", Tcl_GlobObjCmd, NULL, NULL, 0}, - {"load", Tcl_LoadObjCmd, NULL, NULL, 0}, - {"open", Tcl_OpenObjCmd, NULL, NULL, 0}, - {"pid", Tcl_PidObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"puts", Tcl_PutsObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"pwd", Tcl_PwdObjCmd, NULL, NULL, 0}, - {"read", Tcl_ReadObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"seek", Tcl_SeekObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"socket", Tcl_SocketObjCmd, NULL, NULL, 0}, - {"source", Tcl_SourceObjCmd, NULL, TclNRSourceObjCmd, 0}, - {"tell", Tcl_TellObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"time", Tcl_TimeObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"timerate", Tcl_TimeRateObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"unload", Tcl_UnloadObjCmd, NULL, NULL, 0}, - {"update", Tcl_UpdateObjCmd, NULL, NULL, CMD_IS_SAFE}, - {"vwait", Tcl_VwaitObjCmd, NULL, NULL, CMD_IS_SAFE}, - {NULL, NULL, NULL, NULL, 0} -}; - -/* - * Information about which pieces of ensembles to hide when making an - * interpreter safe: - */ - -static const UnsafeEnsembleInfo unsafeEnsembleCommands[] = { - /* [encoding] has two unsafe commands. Assumed by older security policies - * to be overall unsafe; it isn't but... */ - {"encoding", NULL}, - {"encoding", "dirs"}, - {"encoding", "system"}, - /* [file] has MANY unsafe commands! Assumed by older security policies to - * be overall unsafe; it isn't but... */ - {"file", NULL}, - {"file", "atime"}, - {"file", "attributes"}, - {"file", "copy"}, - {"file", "delete"}, - {"file", "dirname"}, - {"file", "executable"}, - {"file", "exists"}, - {"file", "extension"}, - {"file", "isdirectory"}, - {"file", "isfile"}, - {"file", "link"}, - {"file", "lstat"}, - {"file", "mtime"}, - {"file", "mkdir"}, - {"file", "nativename"}, - {"file", "normalize"}, - {"file", "owned"}, - {"file", "readable"}, - {"file", "readlink"}, - {"file", "rename"}, - {"file", "rootname"}, - {"file", "size"}, - {"file", "stat"}, - {"file", "tail"}, - {"file", "tempdir"}, - {"file", "tempfile"}, - {"file", "type"}, - {"file", "volumes"}, - {"file", "writable"}, - /* [info] has two unsafe commands */ - {"info", "cmdtype"}, - {"info", "nameofexecutable"}, - /* [tcl::process] has ONLY unsafe commands! */ - {"process", "list"}, - {"process", "status"}, - {"process", "purge"}, - {"process", "autopurge"}, - /* [zipfs] has MANY unsafe commands! */ - {"zipfs", "lmkimg"}, - {"zipfs", "lmkzip"}, - {"zipfs", "mkimg"}, - {"zipfs", "mkkey"}, - {"zipfs", "mkzip"}, - {"zipfs", "mount"}, - {"zipfs", "mount_data"}, - {"zipfs", "unmount"}, - {NULL, NULL} + {"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} }; /* @@ -488,48 +215,40 @@ static const UnsafeEnsembleInfo unsafeEnsembleCommands[] = { typedef struct { const char *name; /* Name of the function. The full name is - * "::tcl::mathfunc::<name>". */ + * "::tcl::mathfunc::<name>". */ Tcl_ObjCmdProc *objCmdProc; /* Function that evaluates the function */ - double (*fn)(double x); /* Real function pointer */ + ClientData clientData; /* Client data for the function */ } BuiltinFuncDef; static const BuiltinFuncDef BuiltinFuncTable[] = { - { "abs", ExprAbsFunc, NULL }, - { "acos", ExprUnaryFunc, acos }, - { "asin", ExprUnaryFunc, asin }, - { "atan", ExprUnaryFunc, atan }, - { "atan2", ExprBinaryFunc, (double (*)(double))(void *)(double (*)(double, double)) atan2}, + { "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, cos }, - { "cosh", ExprUnaryFunc, cosh }, + { "ceil", ExprCeilFunc, NULL }, + { "cos", ExprUnaryFunc, (ClientData) cos }, + { "cosh", ExprUnaryFunc, (ClientData) cosh }, { "double", ExprDoubleFunc, NULL }, - { "entier", ExprIntFunc, NULL }, - { "exp", ExprUnaryFunc, exp }, - { "floor", ExprFloorFunc, NULL }, - { "fmod", ExprBinaryFunc, (double (*)(double))(void *)(double (*)(double, double)) fmod}, - { "hypot", ExprBinaryFunc, (double (*)(double))(void *)(double (*)(double, double)) hypot}, + { "entier", ExprEntierFunc, NULL }, + { "exp", ExprUnaryFunc, (ClientData) exp }, + { "floor", ExprFloorFunc, NULL }, + { "fmod", ExprBinaryFunc, (ClientData) fmod }, + { "hypot", ExprBinaryFunc, (ClientData) hypot }, { "int", ExprIntFunc, NULL }, - { "isfinite", ExprIsFiniteFunc, NULL }, - { "isinf", ExprIsInfinityFunc, NULL }, - { "isnan", ExprIsNaNFunc, NULL }, - { "isnormal", ExprIsNormalFunc, NULL }, { "isqrt", ExprIsqrtFunc, NULL }, - { "issubnormal", ExprIsSubnormalFunc, NULL, }, - { "isunordered", ExprIsUnorderedFunc, NULL, }, - { "log", ExprUnaryFunc, log }, - { "log10", ExprUnaryFunc, log10 }, - { "max", ExprMaxFunc, NULL }, - { "min", ExprMinFunc, NULL }, - { "pow", ExprBinaryFunc, (double (*)(double))(void *)(double (*)(double, double)) pow}, + { "log", ExprUnaryFunc, (ClientData) log }, + { "log10", ExprUnaryFunc, (ClientData) log10 }, + { "pow", ExprBinaryFunc, (ClientData) pow }, { "rand", ExprRandFunc, NULL }, { "round", ExprRoundFunc, NULL }, - { "sin", ExprUnaryFunc, sin }, - { "sinh", ExprUnaryFunc, sinh }, - { "sqrt", ExprSqrtFunc, NULL }, + { "sin", ExprUnaryFunc, (ClientData) sin }, + { "sinh", ExprUnaryFunc, (ClientData) sinh }, + { "sqrt", ExprSqrtFunc, NULL }, { "srand", ExprSrandFunc, NULL }, - { "tan", ExprUnaryFunc, tan }, - { "tanh", ExprUnaryFunc, tanh }, - { "wide", ExprWideFunc, NULL }, + { "tan", ExprUnaryFunc, (ClientData) tan }, + { "tanh", ExprUnaryFunc, (ClientData) tanh }, + { "wide", ExprWideFunc, NULL }, { NULL, NULL, NULL } }; @@ -595,155 +314,52 @@ static const OpCmdInfo mathOpCmds[] = { /* unused */ {0}, NULL}, { "eq", TclSortingOpCmd, TclCompileStreqOpCmd, /* unused */ {0}, NULL}, - { "lt", TclSortingOpCmd, TclCompileStrLtOpCmd, - /* unused */ {0}, NULL}, - { "le", TclSortingOpCmd, TclCompileStrLeOpCmd, - /* unused */ {0}, NULL}, - { "gt", TclSortingOpCmd, TclCompileStrGtOpCmd, - /* unused */ {0}, NULL}, - { "ge", TclSortingOpCmd, TclCompileStrGeOpCmd, - /* unused */ {0}, NULL}, { NULL, NULL, NULL, {0}, NULL} }; - + /* - *---------------------------------------------------------------------- - * - * TclFinalizeEvaluation -- - * - * Finalizes the script cancellation hash table. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- + * 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. */ -void -TclFinalizeEvaluation(void) -{ - Tcl_MutexLock(&cancelLock); - if (cancelTableInitialized == 1) { - Tcl_DeleteHashTable(&cancelTable); - cancelTableInitialized = 0; - } - Tcl_MutexUnlock(&cancelLock); - - Tcl_MutexLock(&commandTypeLock); - if (commandTypeInit) { - Tcl_DeleteHashTable(&commandTypeTable); - commandTypeInit = 0; - } - Tcl_MutexUnlock(&commandTypeLock); -} - +#if defined(TCL_NO_STACK_CHECK) /* - *---------------------------------------------------------------------- - * - * buildInfoObjCmd -- - * - * Implements tcl::build-info command. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- + * Stack check disabled: make them noops. */ -static int -buildInfoObjCmd( - void *clientData, - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - if (objc > 2) { - Tcl_WrongNumArgs(interp, 1, objv, "?option?"); - return TCL_ERROR; - } - if (objc == 2) { - Tcl_Size len; - const char *arg = TclGetStringFromObj(objv[1], &len); - if (len == 7 && !strcmp(arg, "version")) { - char buf[80]; - const char *p = strchr((char *)clientData, '.'); - if (p) { - const char *q = strchr(p+1, '.'); - const char *r = strchr(p+1, '+'); - p = (q < r) ? q : r; - } - if (p) { - memcpy(buf, (char *)clientData, p - (char *)clientData); - buf[p - (char *)clientData] = '\0'; - Tcl_AppendResult(interp, buf, (void *)NULL); - } - return TCL_OK; - } else if (len == 10 && !strcmp(arg, "patchlevel")) { - char buf[80]; - const char *p = strchr((char *)clientData, '+'); - if (p) { - memcpy(buf, (char *)clientData, p - (char *)clientData); - buf[p - (char *)clientData] = '\0'; - Tcl_AppendResult(interp, buf, (void *)NULL); - } - return TCL_OK; - } else if (len == 6 && !strcmp(arg, "commit")) { - const char *q, *p = strchr((char *)clientData, '+'); - if (p) { - if ((q = strchr(p, '.'))) { - char buf[80]; - memcpy(buf, p+1, q - p - 1); - buf[q - p - 1] = '\0'; - Tcl_AppendResult(interp, buf, (void *)NULL); - } else { - Tcl_AppendResult(interp, p+1, (void *)NULL); - } - } - return TCL_OK; - } else if (len == 8 && !strcmp(arg, "compiler")) { - const char *p = strchr((char *)clientData, '.'); - while (p) { - if (!strncmp(p+1, "clang-", 6) || !strncmp(p+1, "gcc-", 4) - || !strncmp(p+1, "icc-", 4) || !strncmp(p+1, "msvc-", 5)) { - const char *q = strchr(p+1, '.'); - if (q) { - char buf[16]; - memcpy(buf, p+1, q - p - 1); - buf[q - p - 1] = '\0'; - Tcl_AppendResult(interp, buf, (void *)NULL); - } else { - Tcl_AppendResult(interp, p+1, (void *)NULL); - } - return TCL_OK; - } - p = strchr(p+1, '.'); - } - Tcl_AppendResult(interp, "0", (void *)NULL); - return TCL_OK; - } - const char *p = strchr((char *)clientData, '.'); - while (p) { - if (!strncmp(p+1, arg, len) && ((p[len+1] == '.') || (p[len+1] == '\0'))) { - Tcl_AppendResult(interp, "1", (void *)NULL); - return TCL_OK; - } - p = strchr(p+1, '.'); - } - Tcl_AppendResult(interp, "0", (void *)NULL); - return TCL_OK; - } - Tcl_AppendResult(interp, (char *)clientData, (void *)NULL); - return TCL_OK; -} +# 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 */ + +/* *---------------------------------------------------------------------- * * Tcl_CreateInterp -- @@ -770,10 +386,7 @@ Tcl_CreateInterp(void) const BuiltinFuncDef *builtinFuncPtr; const OpCmdInfo *opcmdInfoPtr; const CmdInfo *cmdInfoPtr; - Tcl_Namespace *nsPtr; - Tcl_HashEntry *hPtr; - int isNew; - CancelInfo *cancelInfo; + Tcl_Namespace *mathfuncNSPtr, *mathopNSPtr; union { char c[sizeof(short)]; short s; @@ -783,7 +396,9 @@ Tcl_CreateInterp(void) #endif /* TCL_COMPILE_STATS */ char mathFuncName[32]; CallFrame *framePtr; - const char *version = Tcl_InitSubsystems(); + int result; + + TclInitSubsystems(); /* * Panic if someone updated the CallFrame structure without also updating @@ -791,88 +406,56 @@ Tcl_CreateInterp(void) */ if (sizeof(Tcl_CallFrame) < sizeof(CallFrame)) { + /*NOTREACHED*/ Tcl_Panic("Tcl_CallFrame must not be smaller than CallFrame"); } -#if defined(_WIN32) && !defined(_WIN64) && !defined(_USE_64BIT_TIME_T) - /* If Tcl is compiled on Win32 using -D_USE_64BIT_TIME_T - * the result is a binary incompatible with the 'standard' build of - * Tcl: All extensions using Tcl_StatBuf need to be recompiled in - * the same way. Therefore, this is not officially supported. - * In stead, it is recommended to use Win64 or Tcl 9.0 (not released yet) - */ - if ((offsetof(Tcl_StatBuf,st_atime) != 32) - || (offsetof(Tcl_StatBuf,st_ctime) != 40)) { +#if defined(_WIN32) && !defined(_WIN64) + if (sizeof(time_t) != 4) { + /*NOTREACHED*/ + Tcl_Panic("<time.h> is not compatible with MSVC"); + } + if ((TclOffset(Tcl_StatBuf,st_atime) != 32) + || (TclOffset(Tcl_StatBuf,st_ctime) != 40)) { + /*NOTREACHED*/ Tcl_Panic("<sys/stat.h> is not compatible with MSVC"); } #endif - if (cancelTableInitialized == 0) { - Tcl_MutexLock(&cancelLock); - if (cancelTableInitialized == 0) { - Tcl_InitHashTable(&cancelTable, TCL_ONE_WORD_KEYS); - cancelTableInitialized = 1; - } - - Tcl_MutexUnlock(&cancelLock); - } - -#undef TclObjInterpProc - if (commandTypeInit == 0) { - TclRegisterCommandTypeName(TclObjInterpProc, "proc"); - TclRegisterCommandTypeName(TclEnsembleImplementationCmd, "ensemble"); - TclRegisterCommandTypeName(TclAliasObjCmd, "alias"); - TclRegisterCommandTypeName(TclLocalAliasObjCmd, "alias"); - TclRegisterCommandTypeName(TclChildObjCmd, "interp"); - TclRegisterCommandTypeName(TclInvokeImportedCmd, "import"); - TclRegisterCommandTypeName(TclOOPublicObjectCmd, "object"); - TclRegisterCommandTypeName(TclOOPrivateObjectCmd, "privateObject"); - TclRegisterCommandTypeName(TclOOMyClassObjCmd, "privateClass"); - TclRegisterCommandTypeName(TclNRInterpCoroutine, "coroutine"); - } - /* * 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. */ - iPtr = (Interp *)ckalloc(sizeof(Interp)); + iPtr = (Interp *) ckalloc(sizeof(Interp)); interp = (Tcl_Interp *) iPtr; -#ifdef TCL_NO_DEPRECATED - iPtr->result = &tclEmptyString; -#else iPtr->result = iPtr->resultSpace; -#endif iPtr->freeProc = NULL; iPtr->errorLine = 0; - iPtr->stubTable = &tclStubs; - TclNewObj(iPtr->objResultPtr); + iPtr->objResultPtr = Tcl_NewObj(); Tcl_IncrRefCount(iPtr->objResultPtr); iPtr->handle = TclHandleCreate(iPtr); iPtr->globalNsPtr = NULL; iPtr->hiddenCmdTablePtr = NULL; iPtr->interpInfo = NULL; - TCL_CT_ASSERT(sizeof(iPtr->extra) <= sizeof(Tcl_HashTable)); - iPtr->extra.optimizer = TclOptimizeBytecode; - iPtr->numLevels = 0; iPtr->maxNestingDepth = MAX_NESTING_DEPTH; iPtr->framePtr = NULL; /* Initialise as soon as :: is available */ iPtr->varFramePtr = NULL; /* Initialise as soon as :: is available */ /* - * TIP #280 - Initialize the arrays used to extend the ByteCode and Proc - * structures. + * 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->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); @@ -885,17 +468,6 @@ Tcl_CreateInterp(void) iPtr->errorInfo = NULL; TclNewLiteralStringObj(iPtr->eiVar, "::errorInfo"); Tcl_IncrRefCount(iPtr->eiVar); - iPtr->errorStack = Tcl_NewListObj(0, NULL); - Tcl_IncrRefCount(iPtr->errorStack); - iPtr->resetErrorStack = 1; - TclNewLiteralStringObj(iPtr->upLiteral,"UP"); - Tcl_IncrRefCount(iPtr->upLiteral); - TclNewLiteralStringObj(iPtr->callLiteral,"CALL"); - Tcl_IncrRefCount(iPtr->callLiteral); - TclNewLiteralStringObj(iPtr->innerLiteral,"INNER"); - Tcl_IncrRefCount(iPtr->innerLiteral); - iPtr->innerContext = Tcl_NewListObj(0, NULL); - Tcl_IncrRefCount(iPtr->innerContext); iPtr->errorCode = NULL; TclNewLiteralStringObj(iPtr->ecVar, "::errorCode"); Tcl_IncrRefCount(iPtr->ecVar); @@ -905,30 +477,23 @@ Tcl_CreateInterp(void) iPtr->rootFramePtr = NULL; /* Initialise as soon as :: is available */ iPtr->lookupNsPtr = NULL; -#ifndef TCL_NO_DEPRECATED iPtr->appendResult = NULL; iPtr->appendAvl = 0; iPtr->appendUsed = 0; -#endif Tcl_InitHashTable(&iPtr->packageTable, TCL_STRING_KEYS); iPtr->packageUnknown = NULL; -#ifdef _WIN32 -# define getenv(x) _wgetenv(L##x) /* On Windows, use _wgetenv below */ -#endif - /* TIP #268 */ -#if (TCL_RELEASE_LEVEL == TCL_FINAL_RELEASE) if (getenv("TCL_PKG_PREFER_LATEST") == NULL) { iPtr->packagePrefer = PKG_PREFER_STABLE; - } else -#endif + } else { iPtr->packagePrefer = PKG_PREFER_LATEST; + } iPtr->cmdCount = 0; - TclInitLiteralTable(&iPtr->literalTable); - iPtr->compileEpoch = 1; + TclInitLiteralTable(&(iPtr->literalTable)); + iPtr->compileEpoch = 0; iPtr->compiledProcPtr = NULL; iPtr->resolverPtr = NULL; iPtr->evalFlags = 0; @@ -940,12 +505,10 @@ Tcl_CreateInterp(void) iPtr->activeInterpTracePtr = NULL; iPtr->assocData = NULL; iPtr->execEnvPtr = NULL; /* Set after namespaces initialized. */ - TclNewObj(iPtr->emptyObjPtr); + iPtr->emptyObjPtr = Tcl_NewObj(); /* Another empty object. */ Tcl_IncrRefCount(iPtr->emptyObjPtr); -#ifndef TCL_NO_DEPRECATED iPtr->resultSpace[0] = 0; -#endif iPtr->threadId = Tcl_GetCurrentThread(); /* TIP #378 */ @@ -979,9 +542,12 @@ Tcl_CreateInterp(void) */ /* This is needed to satisfy GCC 3.3's strict aliasing rules */ - framePtr = (CallFrame *)ckalloc(sizeof(CallFrame)); - (void) Tcl_PushCallFrame(interp, (Tcl_CallFrame *) framePtr, + 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; @@ -995,7 +561,7 @@ Tcl_CreateInterp(void) * variable). */ - iPtr->execEnvPtr = TclCreateExecEnv(interp, INTERP_STACK_INITIAL_SIZE); + iPtr->execEnvPtr = TclCreateExecEnv(interp); /* * TIP #219, Tcl Channel Reflection API support. @@ -1004,44 +570,25 @@ Tcl_CreateInterp(void) iPtr->chanMsg = NULL; /* - * TIP #285, Script cancellation support. - */ - - TclNewObj(iPtr->asyncCancelMsg); - - cancelInfo = (CancelInfo *)ckalloc(sizeof(CancelInfo)); - cancelInfo->interp = interp; - - iPtr->asyncCancel = Tcl_AsyncCreate(CancelEvalProc, cancelInfo); - cancelInfo->async = iPtr->asyncCancel; - cancelInfo->result = NULL; - cancelInfo->length = 0; - - Tcl_MutexLock(&cancelLock); - hPtr = Tcl_CreateHashEntry(&cancelTable, iPtr, &isNew); - Tcl_SetHashValue(hPtr, cancelInfo); - Tcl_MutexUnlock(&cancelLock); - - /* * Initialize the compilation and execution statistics kept for this * interpreter. */ #ifdef TCL_COMPILE_STATS - statsPtr = &iPtr->stats; + statsPtr = &(iPtr->stats); statsPtr->numExecutions = 0; statsPtr->numCompilations = 0; statsPtr->numByteCodesFreed = 0; - 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; - memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); - memset(statsPtr->byteCodeCount, 0, sizeof(statsPtr->byteCodeCount)); - memset(statsPtr->lifetimeCount, 0, sizeof(statsPtr->lifetimeCount)); + (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; @@ -1052,14 +599,22 @@ Tcl_CreateInterp(void) statsPtr->numLiteralsCreated = 0; statsPtr->totalLitStringBytes = 0.0; statsPtr->currentLitStringBytes = 0.0; - memset(statsPtr->literalCount, 0, sizeof(statsPtr->literalCount)); + (void) memset(statsPtr->literalCount, 0, sizeof(statsPtr->literalCount)); #endif /* TCL_COMPILE_STATS */ /* + * Initialise the stub table pointer. + */ + + iPtr->stubTable = &tclStubs; + + /* * Initialize the ensemble error message rewriting support. */ - TclResetRewriteEnsemble(interp, 1); + iPtr->ensembleRewrite.sourceObjs = NULL; + iPtr->ensembleRewrite.numRemovedObjs = 0; + iPtr->ensembleRewrite.numInsertedObjs = 0; /* * TIP#143: Initialise the resource limit support. @@ -1068,36 +623,48 @@ Tcl_CreateInterp(void) TclInitLimitSupport(interp); /* - * Initialise the thread-specific data ekeko. Note that the thread's alloc - * cache was already initialised by the call to alloc the interp struct. + * Initialise the thread-specific data ekeko. */ -#if TCL_THREADS && defined(USE_THREAD_ALLOC) - iPtr->allocCache = (AllocCache *)TclpGetAllocCache(); +#if defined(TCL_THREADS) && defined(USE_THREAD_ALLOC) + iPtr->allocCache = TclpGetAllocCache(); #else iPtr->allocCache = NULL; #endif iPtr->pendingObjDataPtr = NULL; iPtr->asyncReadyPtr = TclGetAsyncReadyPtr(); - iPtr->deferredCallbacks = NULL; /* - * Create the core commands. Do it here, rather than calling Tcl_CreateObjCommand, - * because it's faster (there's no need to check for a preexisting command - * by the same name). Set the Tcl_CmdProc to TclInvokeObjectCommand. + * Insure that the stack checking mechanism for this interp is + * initialized. */ - for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { + 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 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->objProc == NULL) - && (cmdInfoPtr->compileProc == NULL) - && (cmdInfoPtr->nreProc == 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, &isNew); if (isNew) { - cmdPtr = (Command *)ckalloc(sizeof(Command)); + cmdPtr = (Command *) ckalloc(sizeof(Command)); cmdPtr->hPtr = hPtr; cmdPtr->nsPtr = iPtr->globalNsPtr; cmdPtr->refCount = 1; @@ -1110,34 +677,22 @@ Tcl_CreateInterp(void) cmdPtr->deleteProc = NULL; cmdPtr->deleteData = NULL; cmdPtr->flags = 0; - if (cmdInfoPtr->flags & CMD_COMPILES_EXPANDED) { - cmdPtr->flags |= CMD_COMPILES_EXPANDED; - } cmdPtr->importRefPtr = NULL; cmdPtr->tracePtr = NULL; - cmdPtr->nreProc = cmdInfoPtr->nreProc; Tcl_SetHashValue(hPtr, cmdPtr); } } /* - * Create the "array", "binary", "chan", "clock", "dict", "encoding", - * "file", "info", "namespace" and "string" ensembles. Note that all these - * commands (and their subcommands that are not present in the global - * namespace) are wholly safe *except* for "clock", "encoding" and "file". + * 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. */ - TclInitArrayCmd(interp); - TclInitBinaryCmd(interp); TclInitChanCmd(interp); TclInitDictCmd(interp); - TclInitEncodingCmd(interp); - TclInitFileCmd(interp); TclInitInfoCmd(interp); - TclInitNamespaceCmd(interp); TclInitStringCmd(interp); - TclInitPrefixCmd(interp); - TclInitProcessCmd(interp); /* * Register "clock" subcommands. These *do* go through @@ -1160,34 +715,11 @@ Tcl_CreateInterp(void) TclDefaultBgErrorHandlerObjCmd, NULL, NULL); /* - * Create unsupported commands for debugging bytecode and objects. + * Create an unsupported command for debugging bytecode. */ Tcl_CreateObjCommand(interp, "::tcl::unsupported::disassemble", - Tcl_DisassembleObjCmd, INT2PTR(0), NULL); - Tcl_CreateObjCommand(interp, "::tcl::unsupported::getbytecode", - Tcl_DisassembleObjCmd, INT2PTR(1), NULL); - Tcl_CreateObjCommand(interp, "::tcl::unsupported::representation", - Tcl_RepresentationCmd, NULL, NULL); - - /* Adding the bytecode assembler command */ - cmdPtr = (Command *) Tcl_NRCreateCommand(interp, - "::tcl::unsupported::assemble", Tcl_AssembleObjCmd, - TclNRAssembleObjCmd, NULL, NULL); - cmdPtr->compileProc = &TclCompileAssembleCmd; - - /* Coroutine monkeybusiness */ - Tcl_NRCreateCommand(interp, "::tcl::unsupported::inject", NULL, - NRInjectObjCmd, NULL, NULL); - Tcl_CreateObjCommand(interp, "::tcl::unsupported::corotype", - CoroTypeObjCmd, NULL, NULL); - - /* Export unsupported commands */ - nsPtr = Tcl_FindNamespace(interp, "::tcl::unsupported", NULL, 0); - if (nsPtr) { - Tcl_Export(interp, nsPtr, "*", 1); - } - + Tcl_DisassembleObjCmd, NULL, NULL); #ifdef USE_DTRACE /* @@ -1201,33 +733,34 @@ Tcl_CreateInterp(void) * Register the builtin math functions. */ - nsPtr = Tcl_CreateNamespace(interp, "::tcl::mathfunc", NULL,NULL); - if (nsPtr == 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::") */ - memcpy(mathFuncName, "::tcl::mathfunc::", MATH_FUNC_PREFIX_LEN); for (builtinFuncPtr = BuiltinFuncTable; builtinFuncPtr->name != NULL; builtinFuncPtr++) { strcpy(mathFuncName+MATH_FUNC_PREFIX_LEN, builtinFuncPtr->name); Tcl_CreateObjCommand(interp, mathFuncName, - builtinFuncPtr->objCmdProc, (void *)builtinFuncPtr->fn, NULL); - Tcl_Export(interp, nsPtr, builtinFuncPtr->name, 0); + builtinFuncPtr->objCmdProc, builtinFuncPtr->clientData, NULL); + Tcl_Export(interp, mathfuncNSPtr, builtinFuncPtr->name, 0); } /* * Register the mathematical "operator" commands. [TIP #174] */ - nsPtr = Tcl_CreateNamespace(interp, "::tcl::mathop", NULL, NULL); - if (nsPtr == NULL) { + 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"); } - Tcl_Export(interp, nsPtr, "*", 1); -#define MATH_OP_PREFIX_LEN 15 /* == strlen("::tcl::mathop::") */ - memcpy(mathFuncName, "::tcl::mathop::", MATH_OP_PREFIX_LEN); + (void) Tcl_Export(interp, mathopNSPtr, "*", 1); + strcpy(mathFuncName, "::tcl::mathop::"); for (opcmdInfoPtr=mathOpCmds ; opcmdInfoPtr->name!=NULL ; opcmdInfoPtr++){ - TclOpCmdClientData *occdPtr = (TclOpCmdClientData *)ckalloc(sizeof(TclOpCmdClientData)); + TclOpCmdClientData *occdPtr = (TclOpCmdClientData *) + ckalloc(sizeof(TclOpCmdClientData)); occdPtr->op = opcmdInfoPtr->name; occdPtr->i.numArgs = opcmdInfoPtr->i.numArgs; @@ -1273,26 +806,24 @@ Tcl_CreateInterp(void) TCL_GLOBAL_ONLY); Tcl_SetVar2Ex(interp, "tcl_platform", "wordSize", - Tcl_NewWideIntObj(sizeof(long)), TCL_GLOBAL_ONLY); + Tcl_NewLongObj((long) sizeof(long)), TCL_GLOBAL_ONLY); /* TIP #291 */ Tcl_SetVar2Ex(interp, "tcl_platform", "pointerSize", - Tcl_NewWideIntObj(sizeof(void *)), TCL_GLOBAL_ONLY); + Tcl_NewLongObj((long) sizeof(void *)), TCL_GLOBAL_ONLY); /* * Set up other variables such as tcl_version and tcl_library */ - Tcl_SetVar2(interp, "tcl_patchLevel", NULL, TCL_PATCH_LEVEL, TCL_GLOBAL_ONLY); - Tcl_SetVar2(interp, "tcl_version", NULL, TCL_VERSION, TCL_GLOBAL_ONLY); -#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 + 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", NULL, TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, TclPrecTraceProc, NULL); -#endif /* !TCL_NO_DEPRECATED */ TclpSetVariables(interp); -#if TCL_THREADS && !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 +#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 @@ -1306,112 +837,29 @@ Tcl_CreateInterp(void) /* * Register Tcl's version number. * TIP #268: Full patchlevel instead of just major.minor - * TIP #599: Extended build information "+<UUID>.<tag1>.<tag2>...." */ Tcl_PkgProvideEx(interp, "Tcl", TCL_PATCH_LEVEL, &tclStubs); - Tcl_PkgProvideEx(interp, "tcl", TCL_PATCH_LEVEL, &tclStubs); - Tcl_CreateObjCommand(interp, "::tcl::build-info", - buildInfoObjCmd, (void *)version, NULL); - - if (TclTommath_Init(interp) != TCL_OK) { - Tcl_Panic("%s", Tcl_GetStringResult(interp)); - } - - if (TclOOInit(interp) != TCL_OK) { - Tcl_Panic("%s", Tcl_GetStringResult(interp)); - } - /* - * Only build in zlib support if we've successfully detected a library to - * compile and link against. - */ +#ifdef Tcl_InitStubs +#undef Tcl_InitStubs +#endif + Tcl_InitStubs(interp, TCL_VERSION, 1); -#ifdef HAVE_ZLIB - if (TclZlibInit(interp) != TCL_OK) { - Tcl_Panic("%s", Tcl_GetStringResult(interp)); - } - if (TclZipfs_Init(interp) != TCL_OK) { - Tcl_Panic("%s", Tcl_GetStringResult(interp)); + if (TclTommath_Init(interp) != TCL_OK) { + Tcl_Panic("%s", Tcl_GetString(Tcl_GetObjResult(interp))); } -#endif - TOP_CB(iPtr) = NULL; return interp; } static void DeleteOpCmdClientData( - void *clientData) -{ - TclOpCmdClientData *occdPtr = (TclOpCmdClientData *)clientData; - - ckfree(occdPtr); -} - -/* - * --------------------------------------------------------------------- - * - * TclRegisterCommandTypeName, TclGetCommandTypeName -- - * - * Command type registration and lookup mechanism. Everything is keyed by - * the Tcl_ObjCmdProc for the command, and that is used as the *key* into - * the hash table that maps to constant strings that are names. (It is - * recommended that those names be ASCII.) - * - * --------------------------------------------------------------------- - */ - -void -TclRegisterCommandTypeName( - Tcl_ObjCmdProc *implementationProc, - const char *nameStr) -{ - Tcl_HashEntry *hPtr; - - Tcl_MutexLock(&commandTypeLock); - if (commandTypeInit == 0) { - Tcl_InitHashTable(&commandTypeTable, TCL_ONE_WORD_KEYS); - commandTypeInit = 1; - } - if (nameStr != NULL) { - int isNew; - - hPtr = Tcl_CreateHashEntry(&commandTypeTable, - implementationProc, &isNew); - Tcl_SetHashValue(hPtr, (void *) nameStr); - } else { - hPtr = Tcl_FindHashEntry(&commandTypeTable, - implementationProc); - if (hPtr != NULL) { - Tcl_DeleteHashEntry(hPtr); - } - } - Tcl_MutexUnlock(&commandTypeLock); -} - -const char * -TclGetCommandTypeName( - Tcl_Command command) + ClientData clientData) { - Command *cmdPtr = (Command *) command; - Tcl_ObjCmdProc *procPtr = cmdPtr->objProc; - const char *name = "native"; - - if (procPtr == NULL) { - procPtr = cmdPtr->nreProc; - } - Tcl_MutexLock(&commandTypeLock); - if (commandTypeInit) { - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&commandTypeTable, procPtr); - - if (hPtr && Tcl_GetHashValue(hPtr)) { - name = (const char *) Tcl_GetHashValue(hPtr); - } - } - Tcl_MutexUnlock(&commandTypeLock); + TclOpCmdClientData *occdPtr = clientData; - return name; + ckfree((char *) occdPtr); } /* @@ -1434,94 +882,20 @@ int TclHideUnsafeCommands( Tcl_Interp *interp) /* Hide commands in this interpreter. */ { - const CmdInfo *cmdInfoPtr; - const UnsafeEnsembleInfo *unsafePtr; + register const CmdInfo *cmdInfoPtr; if (interp == NULL) { return TCL_ERROR; } for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { - if (!(cmdInfoPtr->flags & CMD_IS_SAFE)) { + if (!cmdInfoPtr->isSafe) { Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); } } - - for (unsafePtr = unsafeEnsembleCommands; - unsafePtr->ensembleNsName; unsafePtr++) { - if (unsafePtr->commandName) { - /* - * Hide an ensemble subcommand. - */ - - Tcl_Obj *cmdName = Tcl_ObjPrintf("::tcl::%s::%s", - unsafePtr->ensembleNsName, unsafePtr->commandName); - Tcl_Obj *hideName = Tcl_ObjPrintf("tcl:%s:%s", - unsafePtr->ensembleNsName, unsafePtr->commandName); - - if (TclRenameCommand(interp, TclGetString(cmdName), - "___tmp") != TCL_OK - || Tcl_HideCommand(interp, "___tmp", - TclGetString(hideName)) != TCL_OK) { - Tcl_Panic("problem making '%s %s' safe: %s", - unsafePtr->ensembleNsName, unsafePtr->commandName, - Tcl_GetStringResult(interp)); - } - Tcl_CreateObjCommand(interp, TclGetString(cmdName), - BadEnsembleSubcommand, (void *)unsafePtr, NULL); - TclDecrRefCount(cmdName); - TclDecrRefCount(hideName); - } else { - /* - * Hide an ensemble main command (for compatibility). - */ - - if (Tcl_HideCommand(interp, unsafePtr->ensembleNsName, - unsafePtr->ensembleNsName) != TCL_OK) { - Tcl_Panic("problem making '%s' safe: %s", - unsafePtr->ensembleNsName, - Tcl_GetStringResult(interp)); - } - } - } - return TCL_OK; } /* - *---------------------------------------------------------------------- - * - * BadEnsembleSubcommand -- - * - * Command used to act as a backstop implementation when subcommands of - * ensembles are unsafe (the real implementations of the subcommands are - * hidden). The clientData is description of what was hidden. - * - * Results: - * A standard Tcl result (always a TCL_ERROR). - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -BadEnsembleSubcommand( - void *clientData, - Tcl_Interp *interp, - TCL_UNUSED(int) /*objc*/, - TCL_UNUSED(Tcl_Obj *const *) /* objv */) -{ - const UnsafeEnsembleInfo *infoPtr = (const UnsafeEnsembleInfo *)clientData; - - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "not allowed to invoke subcommand %s of %s", - infoPtr->commandName, infoPtr->ensembleNsName)); - Tcl_SetErrorCode(interp, "TCL", "SAFE", "SUBCOMMAND", (void *)NULL); - return TCL_ERROR; -} - -/* *-------------------------------------------------------------- * * Tcl_CallWhenDeleted -- @@ -1547,22 +921,22 @@ Tcl_CallWhenDeleted( Tcl_Interp *interp, /* Interpreter to watch. */ Tcl_InterpDeleteProc *proc, /* Function to call when interpreter is about * to be deleted. */ - void *clientData) /* One-word value to pass to proc. */ + ClientData clientData) /* One-word value to pass to proc. */ { Interp *iPtr = (Interp *) interp; static Tcl_ThreadDataKey assocDataCounterKey; int *assocDataCounterPtr = - (int *)Tcl_GetThreadData(&assocDataCounterKey, sizeof(int)); + Tcl_GetThreadData(&assocDataCounterKey, (int)sizeof(int)); int isNew; char buffer[32 + TCL_INTEGER_SPACE]; - AssocData *dPtr = (AssocData *)ckalloc(sizeof(AssocData)); + AssocData *dPtr = (AssocData *) ckalloc(sizeof(AssocData)); Tcl_HashEntry *hPtr; - snprintf(buffer, sizeof(buffer), "Assoc Data Key #%d", *assocDataCounterPtr); + sprintf(buffer, "Assoc Data Key #%d", *assocDataCounterPtr); (*assocDataCounterPtr)++; if (iPtr->assocData == NULL) { - iPtr->assocData = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); + iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); } hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &isNew); @@ -1595,7 +969,7 @@ Tcl_DontCallWhenDeleted( Tcl_Interp *interp, /* Interpreter to watch. */ Tcl_InterpDeleteProc *proc, /* Function to call when interpreter is about * to be deleted. */ - void *clientData) /* One-word value to pass to proc. */ + ClientData clientData) /* One-word value to pass to proc. */ { Interp *iPtr = (Interp *) interp; Tcl_HashTable *hTablePtr; @@ -1609,9 +983,9 @@ Tcl_DontCallWhenDeleted( } for (hPtr = Tcl_FirstHashEntry(hTablePtr, &hSearch); hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { - dPtr = (AssocData *)Tcl_GetHashValue(hPtr); + dPtr = (AssocData *) Tcl_GetHashValue(hPtr); if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { - ckfree(dPtr); + ckfree((char *) dPtr); Tcl_DeleteHashEntry(hPtr); return; } @@ -1643,7 +1017,7 @@ Tcl_SetAssocData( const char *name, /* Name for association. */ Tcl_InterpDeleteProc *proc, /* Proc to call when interpreter is about to * be deleted. */ - void *clientData) /* One-word value to pass to proc. */ + ClientData clientData) /* One-word value to pass to proc. */ { Interp *iPtr = (Interp *) interp; AssocData *dPtr; @@ -1651,14 +1025,14 @@ Tcl_SetAssocData( int isNew; if (iPtr->assocData == NULL) { - iPtr->assocData = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); + iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); } hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &isNew); if (isNew == 0) { - dPtr = (AssocData *)Tcl_GetHashValue(hPtr); + dPtr = Tcl_GetHashValue(hPtr); } else { - dPtr = (AssocData *)ckalloc(sizeof(AssocData)); + dPtr = (AssocData *) ckalloc(sizeof(AssocData)); } dPtr->proc = proc; dPtr->clientData = clientData; @@ -1699,12 +1073,12 @@ Tcl_DeleteAssocData( if (hPtr == NULL) { return; } - dPtr = (AssocData *)Tcl_GetHashValue(hPtr); - Tcl_DeleteHashEntry(hPtr); + dPtr = Tcl_GetHashValue(hPtr); if (dPtr->proc != NULL) { dPtr->proc(dPtr->clientData, interp); } - ckfree(dPtr); + ckfree((char *) dPtr); + Tcl_DeleteHashEntry(hPtr); } /* @@ -1725,7 +1099,7 @@ Tcl_DeleteAssocData( *---------------------------------------------------------------------- */ -void * +ClientData Tcl_GetAssocData( Tcl_Interp *interp, /* Interpreter associated with. */ const char *name, /* Name of association. */ @@ -1744,7 +1118,7 @@ Tcl_GetAssocData( if (hPtr == NULL) { return NULL; } - dPtr = (AssocData *)Tcl_GetHashValue(hPtr); + dPtr = Tcl_GetHashValue(hPtr); if (procPtr != NULL) { *procPtr = dPtr->proc; } @@ -1825,7 +1199,7 @@ Tcl_DeleteInterp( * Ensure that the interpreter is eventually deleted. */ - Tcl_EventuallyFree(interp, DeleteInterpProc); + Tcl_EventuallyFree(interp, (Tcl_FreeProc *) DeleteInterpProc); } /* @@ -1851,22 +1225,19 @@ Tcl_DeleteInterp( static void DeleteInterpProc( - char *blockPtr) /* Interpreter to delete. */ + Tcl_Interp *interp) /* Interpreter to delete. */ { - Tcl_Interp *interp = (Tcl_Interp *) blockPtr; Interp *iPtr = (Interp *) interp; Tcl_HashEntry *hPtr; Tcl_HashSearch search; Tcl_HashTable *hTablePtr; ResolverScheme *resPtr, *nextResPtr; - Tcl_Size i; /* - * Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup, - * unless we are exiting. + * Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup. */ - if ((iPtr->numLevels > 0) && !TclInExit()) { + if (iPtr->numLevels > 0) { Tcl_Panic("DeleteInterpProc called with active evals"); } @@ -1889,37 +1260,6 @@ DeleteInterpProc( } /* - * TIP #285, Script cancellation support. Delete this interp from the - * global hash table of CancelInfo structs. - */ - - Tcl_MutexLock(&cancelLock); - hPtr = Tcl_FindHashEntry(&cancelTable, iPtr); - if (hPtr != NULL) { - CancelInfo *cancelInfo = (CancelInfo *)Tcl_GetHashValue(hPtr); - - if (cancelInfo != NULL) { - if (cancelInfo->result != NULL) { - ckfree(cancelInfo->result); - } - ckfree(cancelInfo); - } - - Tcl_DeleteHashEntry(hPtr); - } - - if (iPtr->asyncCancel != NULL) { - Tcl_AsyncDelete(iPtr->asyncCancel); - iPtr->asyncCancel = NULL; - } - - if (iPtr->asyncCancelMsg != NULL) { - Tcl_DecrRefCount(iPtr->asyncCancelMsg); - iPtr->asyncCancelMsg = NULL; - } - Tcl_MutexUnlock(&cancelLock); - - /* * Shut down all limit handler callback scripts that call back into this * interpreter. Then eliminate all limit handlers for this interpreter. */ @@ -1948,40 +1288,41 @@ DeleteInterpProc( /* * 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 + * Tcl_DeleteCommandFromToken() will remove the entry from the * hiddenCmdTablePtr. */ hPtr = Tcl_FirstHashEntry(hTablePtr, &search); for (; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { - Tcl_DeleteCommandFromToken(interp, (Tcl_Command)Tcl_GetHashValue(hPtr)); + Tcl_DeleteCommandFromToken(interp, + (Tcl_Command) Tcl_GetHashValue(hPtr)); } Tcl_DeleteHashTable(hTablePtr); - ckfree(hTablePtr); + ckfree((char *) hTablePtr); } + /* + * Invoke deletion callbacks; note that a callback can create new + * callbacks, so we iterate. + */ - if (iPtr->assocData != NULL) { + while (iPtr->assocData != NULL) { AssocData *dPtr; hTablePtr = iPtr->assocData; - /* - * Invoke deletion callbacks; note that a callback can create new - * callbacks, so we iterate. - */ + iPtr->assocData = NULL; for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); hPtr != NULL; hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { - dPtr = (AssocData *)Tcl_GetHashValue(hPtr); + dPtr = Tcl_GetHashValue(hPtr); + Tcl_DeleteHashEntry(hPtr); if (dPtr->proc != NULL) { dPtr->proc(dPtr->clientData, interp); } - Tcl_DeleteHashEntry(hPtr); - ckfree(dPtr); + ckfree((char *) dPtr); } Tcl_DeleteHashTable(hTablePtr); - ckfree(hTablePtr); - iPtr->assocData = NULL; + ckfree((char *) hTablePtr); } /* @@ -1989,11 +1330,11 @@ DeleteInterpProc( * namespace. The order is important [Bug 1658572]. */ - if ((iPtr->framePtr != iPtr->rootFramePtr) && !TclInExit()) { + if (iPtr->framePtr != iPtr->rootFramePtr) { Tcl_Panic("DeleteInterpProc: popping rootCallFrame with other frames on top"); } Tcl_PopCallFrame(interp); - ckfree(iPtr->rootFramePtr); + ckfree((char *) iPtr->rootFramePtr); iPtr->rootFramePtr = NULL; Tcl_DeleteNamespace((Tcl_Namespace *) iPtr->globalNsPtr); @@ -2002,10 +1343,8 @@ DeleteInterpProc( * could have transferred ownership of the result string to Tcl. */ -#ifndef TCL_NO_DEPRECATED Tcl_FreeResult(interp); - iPtr->result = NULL; -#endif + interp->result = NULL; Tcl_DecrRefCount(iPtr->objResultPtr); iPtr->objResultPtr = NULL; Tcl_DecrRefCount(iPtr->ecVar); @@ -2018,21 +1357,13 @@ DeleteInterpProc( Tcl_DecrRefCount(iPtr->errorInfo); iPtr->errorInfo = NULL; } - Tcl_DecrRefCount(iPtr->errorStack); - iPtr->errorStack = NULL; - Tcl_DecrRefCount(iPtr->upLiteral); - Tcl_DecrRefCount(iPtr->callLiteral); - Tcl_DecrRefCount(iPtr->innerLiteral); - Tcl_DecrRefCount(iPtr->innerContext); if (iPtr->returnOpts) { Tcl_DecrRefCount(iPtr->returnOpts); } -#ifndef TCL_NO_DEPRECATED if (iPtr->appendResult != NULL) { ckfree(iPtr->appendResult); iPtr->appendResult = NULL; } -#endif TclFreePackageInfo(iPtr); while (iPtr->tracePtr != NULL) { Tcl_DeleteTrace((Tcl_Interp *) iPtr, (Tcl_Trace) iPtr->tracePtr); @@ -2051,7 +1382,7 @@ DeleteInterpProc( while (resPtr) { nextResPtr = resPtr->nextPtr; ckfree(resPtr->name); - ckfree(resPtr); + ckfree((char *) resPtr); resPtr = nextResPtr; } @@ -2060,101 +1391,104 @@ DeleteInterpProc( * interpreter. */ - TclDeleteLiteralTable(interp, &iPtr->literalTable); + TclDeleteLiteralTable(interp, &(iPtr->literalTable)); /* * TIP #280 - Release the arrays for ByteCode/Proc extension, and * contents. */ - for (hPtr = Tcl_FirstHashEntry(iPtr->linePBodyPtr, &search); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&search)) { - CmdFrame *cfPtr = (CmdFrame *)Tcl_GetHashValue(hPtr); - Proc *procPtr = (Proc *) Tcl_GetHashKey(iPtr->linePBodyPtr, hPtr); + { + Tcl_HashEntry *hPtr; + Tcl_HashSearch hSearch; + int i; - procPtr->iPtr = NULL; - if (cfPtr) { - if (cfPtr->type == TCL_LOCATION_SOURCE) { - Tcl_DecrRefCount(cfPtr->data.eval.path); + for (hPtr = Tcl_FirstHashEntry(iPtr->linePBodyPtr, &hSearch); + 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(cfPtr->line); - ckfree(cfPtr); + Tcl_DeleteHashEntry(hPtr); } - Tcl_DeleteHashEntry(hPtr); - } - Tcl_DeleteHashTable(iPtr->linePBodyPtr); - ckfree(iPtr->linePBodyPtr); - iPtr->linePBodyPtr = NULL; + 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, &search); - hPtr != NULL; - hPtr = Tcl_NextHashEntry(&search)) { - ExtCmdLoc *eclPtr = (ExtCmdLoc *)Tcl_GetHashValue(hPtr); + for (hPtr = Tcl_FirstHashEntry(iPtr->lineBCPtr, &hSearch); + hPtr != NULL; + hPtr = Tcl_NextHashEntry(&hSearch)) { + ExtCmdLoc *eclPtr = (ExtCmdLoc *) Tcl_GetHashValue(hPtr); - if (eclPtr->type == TCL_LOCATION_SOURCE) { - Tcl_DecrRefCount(eclPtr->path); - } - for (i=0; i<eclPtr->nuloc; i++) { - ckfree(eclPtr->loc[i].line); - } + if (eclPtr->type == TCL_LOCATION_SOURCE) { + Tcl_DecrRefCount(eclPtr->path); + } + for (i=0; i< eclPtr->nuloc; i++) { + ckfree((char *) eclPtr->loc[i].line); + } - if (eclPtr->loc != NULL) { - ckfree(eclPtr->loc); - } + if (eclPtr->loc != NULL) { + ckfree((char *) eclPtr->loc); + } - ckfree(eclPtr); - Tcl_DeleteHashEntry(hPtr); - } - Tcl_DeleteHashTable(iPtr->lineBCPtr); - ckfree(iPtr->lineBCPtr); - iPtr->lineBCPtr = NULL; + Tcl_DeleteHashTable (&eclPtr->litInfo); - /* - * Location stack for uplevel/eval/... scripts which were passed through - * proc arguments. Actually we track all arguments as we do not and cannot - * know which arguments will be used as scripts and which will not. - */ + ckfree((char *) eclPtr); + Tcl_DeleteHashEntry(hPtr); + } + Tcl_DeleteHashTable(iPtr->lineBCPtr); + ckfree((char *) iPtr->lineBCPtr); + iPtr->lineBCPtr = NULL; - if (iPtr->lineLAPtr->numEntries && !TclInExit()) { /* - * When the interp goes away we have nothing on the stack, so there - * are no arguments, so this table has to be empty. + * Location stack for uplevel/eval/... scripts which were passed + * through proc arguments. Actually we track all arguments as we + * don't, cannot know which arguments will be used as scripts and + * which won't. */ - Tcl_Panic("Argument location tracking table not empty"); - } + if (iPtr->lineLAPtr->numEntries) { + /* + * When the interp goes away we have nothing on the stack, so + * there are no arguments, so this table has to be empty. + */ - Tcl_DeleteHashTable(iPtr->lineLAPtr); - ckfree(iPtr->lineLAPtr); - iPtr->lineLAPtr = NULL; + Tcl_Panic ("Argument location tracking table not empty"); + } - if (iPtr->lineLABCPtr->numEntries && !TclInExit()) { - /* - * When the interp goes away we have nothing on the stack, so there - * are no arguments, so this table has to be empty. - */ + Tcl_DeleteHashTable (iPtr->lineLAPtr); + ckfree((char*) iPtr->lineLAPtr); + iPtr->lineLAPtr = NULL; - Tcl_Panic("Argument location tracking table not empty"); - } + if (iPtr->lineLABCPtr->numEntries) { + /* + * When the interp goes away we have nothing on the stack, so + * there are no arguments, so this table has to be empty. + */ - Tcl_DeleteHashTable(iPtr->lineLABCPtr); - ckfree(iPtr->lineLABCPtr); - iPtr->lineLABCPtr = NULL; + Tcl_Panic ("Argument location tracking table not empty"); + } - /* - * Squelch the tables of traces on variables and searches over arrays in - * the in the interpreter. - */ + Tcl_DeleteHashTable (iPtr->lineLABCPtr); + ckfree((char*) iPtr->lineLABCPtr); + iPtr->lineLABCPtr = NULL; + } Tcl_DeleteHashTable(&iPtr->varTraces); Tcl_DeleteHashTable(&iPtr->varSearches); - ckfree(iPtr); + ckfree((char *) iPtr); } /* @@ -2220,10 +1554,9 @@ Tcl_HideCommand( */ if (strstr(hiddenCmdToken, "::") != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( + Tcl_AppendResult(interp, "cannot use namespace qualifiers in hidden command" - " token (rename)", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "HIDDENTOKEN", (void *)NULL); + " token (rename)", NULL); return TCL_ERROR; } @@ -2245,10 +1578,8 @@ Tcl_HideCommand( */ if (cmdPtr->nsPtr != iPtr->globalNsPtr) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can only hide global namespace commands (use rename then hide)", - TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "HIDE", "NON_GLOBAL", (void *)NULL); + Tcl_AppendResult(interp, "can only hide global namespace commands" + " (use rename then hide)", NULL); return TCL_ERROR; } @@ -2258,7 +1589,8 @@ Tcl_HideCommand( hiddenCmdTablePtr = iPtr->hiddenCmdTablePtr; if (hiddenCmdTablePtr == NULL) { - hiddenCmdTablePtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); + hiddenCmdTablePtr = (Tcl_HashTable *) + ckalloc((unsigned) sizeof(Tcl_HashTable)); Tcl_InitHashTable(hiddenCmdTablePtr, TCL_STRING_KEYS); iPtr->hiddenCmdTablePtr = hiddenCmdTablePtr; } @@ -2271,23 +1603,21 @@ Tcl_HideCommand( hPtr = Tcl_CreateHashEntry(hiddenCmdTablePtr, hiddenCmdToken, &isNew); if (!isNew) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "hidden command named \"%s\" already exists", - hiddenCmdToken)); - Tcl_SetErrorCode(interp, "TCL", "HIDE", "ALREADY_HIDDEN", (void *)NULL); + Tcl_AppendResult(interp, "hidden command named \"", hiddenCmdToken, + "\" already exists", NULL); return TCL_ERROR; } /* - * NB: This code is currently 'like' a rename to a special separate name + * 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. */ /* * Remove the hash entry for the command from the interpreter command - * table. This is like deleting the command, so bump its command epoch - * to invalidate any cached references that point to the command. + * table. This is like deleting the command, so bump its command epoch; + * this invalidates any cached references that point to the command. */ if (cmdPtr->hPtr != NULL) { @@ -2375,10 +1705,8 @@ Tcl_ExposeCommand( */ if (strstr(cmdName, "::") != NULL) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "cannot expose to a namespace (use expose to toplevel, then rename)", - TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "EXPOSE", "NON_GLOBAL", (void *)NULL); + Tcl_AppendResult(interp, "cannot expose to a namespace " + "(use expose to toplevel, then rename)", NULL); return TCL_ERROR; } @@ -2392,29 +1720,27 @@ Tcl_ExposeCommand( hPtr = Tcl_FindHashEntry(hiddenCmdTablePtr, hiddenCmdToken); } if (hPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown hidden command \"%s\"", hiddenCmdToken)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "HIDDENTOKEN", - hiddenCmdToken, (void *)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 + * 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 theoretically impossible, we might rather Tcl_Panic + * This case is theoritically impossible, we might rather Tcl_Panic() * than 'nicely' erroring out ? */ - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "trying to expose a non-global command namespace command", - TCL_INDEX_NONE)); + Tcl_AppendResult(interp, + "trying to expose a non global command name space command", + NULL); return TCL_ERROR; } @@ -2431,24 +1757,12 @@ Tcl_ExposeCommand( hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &isNew); if (!isNew) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "exposed command \"%s\" already exists", cmdName)); - Tcl_SetErrorCode(interp, "TCL", "EXPOSE", "COMMAND_EXISTS", (void *)NULL); + Tcl_AppendResult(interp, "exposed command \"", cmdName, + "\" already exists", NULL); return TCL_ERROR; } /* - * Command resolvers (per-interp, per-namespace) might have resolved to a - * command for the given namespace scope with this command not being - * registered with the namespace's command table. During BC compilation, - * the so-resolved command turns into a CmdName literal. Without - * invalidating a possible CmdName literal here explicitly, such literals - * keep being reused while pointing to overhauled commands. - */ - - TclInvalidateCmdLiteral(interp, cmdName, nsPtr); - - /* * 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. @@ -2514,7 +1828,7 @@ Tcl_ExposeCommand( * 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 eventually calls proc. When the command + * (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. * @@ -2530,18 +1844,18 @@ Tcl_CreateCommand( * specified namespace; otherwise it is put in * the global namespace. */ Tcl_CmdProc *proc, /* Function to associate with cmdName. */ - void *clientData, /* Arbitrary value passed to string proc. */ + 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; - Command *cmdPtr; + Namespace *nsPtr, *dummy1, *dummy2; + Command *cmdPtr, *refCmdPtr; Tcl_HashEntry *hPtr; const char *tail; - int isNew = 0, deleted = 0; + int isNew; ImportedCmdData *dataPtr; if (iPtr->flags & DELETED) { @@ -2554,54 +1868,32 @@ Tcl_CreateCommand( } /* - * If the command name we seek to create already exists, we need to - * delete that first. That can be tricky in the presence of traces. - * Loop until we no longer find an existing command in the way, or - * until we've deleted one command and that didn't finish the job. + * 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. */ - while (1) { - /* - * 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) { - Namespace *dummy1, *dummy2; - - 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, &isNew); - - if (isNew || deleted) { - /* - * isNew - No conflict with existing command. - * deleted - We've already deleted a conflicting command - */ - break; + if (strstr(cmdName, "::") != 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, &isNew); + if (!isNew) { /* - * An existing command conflicts. Try to delete it... - */ - - cmdPtr = (Command *)Tcl_GetHashValue(hPtr); - - /* - * 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 = Tcl_GetHashValue(hPtr); cmdPtr->refCount++; if (cmdPtr->importRefPtr) { cmdPtr->flags |= CMD_REDEF_IN_PROGRESS; @@ -2614,32 +1906,18 @@ Tcl_CreateCommand( cmdPtr->importRefPtr = NULL; } TclCleanupCommandMacro(cmdPtr); - deleted = 1; - } - - 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). - */ - - ckfree(Tcl_GetHashValue(hPtr)); - } - if (!deleted) { - /* - * Command resolvers (per-interp, per-namespace) might have resolved - * to a command for the given namespace scope with this command not - * being registered with the namespace's command table. During BC - * compilation, the so-resolved command turns into a CmdName literal. - * Without invalidating a possible CmdName literal here explicitly, - * such literals keep being reused while pointing to overhauled - * commands. - */ - - TclInvalidateCmdLiteral(interp, tail, nsPtr); + 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). + */ + 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 @@ -2649,7 +1927,7 @@ Tcl_CreateCommand( TclInvalidateNsCmdLookup(nsPtr); TclInvalidateNsPath(nsPtr); } - cmdPtr = (Command *)ckalloc(sizeof(Command)); + cmdPtr = (Command *) ckalloc(sizeof(Command)); Tcl_SetHashValue(hPtr, cmdPtr); cmdPtr->hPtr = hPtr; cmdPtr->nsPtr = nsPtr; @@ -2665,7 +1943,6 @@ Tcl_CreateCommand( cmdPtr->flags = 0; cmdPtr->importRefPtr = NULL; cmdPtr->tracePtr = NULL; - cmdPtr->nreProc = NULL; /* * Plug in any existing import references found above. Be sure to update @@ -2675,8 +1952,8 @@ Tcl_CreateCommand( if (oldRefPtr != NULL) { cmdPtr->importRefPtr = oldRefPtr; while (oldRefPtr != NULL) { - Command *refCmdPtr = oldRefPtr->importedCmdPtr; - dataPtr = (ImportedCmdData *)refCmdPtr->objClientData; + refCmdPtr = oldRefPtr->importedCmdPtr; + dataPtr = refCmdPtr->objClientData; dataPtr->realCmdPtr = cmdPtr; oldRefPtr = oldRefPtr->nextPtr; } @@ -2728,36 +2005,39 @@ Tcl_CreateObjCommand( * the global namespace. */ Tcl_ObjCmdProc *proc, /* Object-based function to associate with * name. */ - void *clientData, /* Arbitrary value to pass to object - * function. */ - Tcl_CmdDeleteProc *deleteProc + 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; - Namespace *nsPtr; + ImportRef *oldRefPtr = NULL; + Namespace *nsPtr, *dummy1, *dummy2; + Command *cmdPtr, *refCmdPtr; + Tcl_HashEntry *hPtr; 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. */ + 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. + * namespace qualifiers, we put it in the specified namespace; otherwise, + * we always put it in the global namespace. */ if (strstr(cmdName, "::") != NULL) { - Namespace *dummy1, *dummy2; - TclGetNamespaceForQualName(interp, cmdName, NULL, - TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); + TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); if ((nsPtr == NULL) || (tail == NULL)) { return (Tcl_Command) NULL; } @@ -2766,61 +2046,19 @@ Tcl_CreateObjCommand( tail = cmdName; } - return TclCreateObjCommandInNs(interp, tail, (Tcl_Namespace *) nsPtr, - proc, clientData, deleteProc); -} - -Tcl_Command -TclCreateObjCommandInNs( - Tcl_Interp *interp, - const char *cmdName, /* Name of command, without any namespace - * components. */ - Tcl_Namespace *namesp, /* The namespace to create the command in */ - Tcl_ObjCmdProc *proc, /* Object-based function to associate with - * name. */ - void *clientData, /* Arbitrary value to pass to object - * function. */ - Tcl_CmdDeleteProc *deleteProc) - /* If not NULL, gives a function to call when - * this command is deleted. */ -{ - int deleted = 0, isNew = 0; - Command *cmdPtr; - ImportRef *oldRefPtr = NULL; - ImportedCmdData *dataPtr; - Tcl_HashEntry *hPtr; - Namespace *nsPtr = (Namespace *) namesp; - - /* - * If the command name we seek to create already exists, we need to delete - * that first. That can be tricky in the presence of traces. Loop until we - * no longer find an existing command in the way, or until we've deleted - * one command and that didn't finish the job. - */ - - while (1) { - hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &isNew); - - if (isNew || deleted) { - /* - * isNew - No conflict with existing command. - * deleted - We've already deleted a conflicting command - */ - break; - } - - /* - * An existing command conflicts. Try to delete it... - */ + hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); + TclInvalidateNsPath(nsPtr); + if (!isNew) { + cmdPtr = Tcl_GetHashValue(hPtr); - cmdPtr = (Command *)Tcl_GetHashValue(hPtr); + /* Command already exists. */ /* - * [***] This is wrong. See Tcl Bug a16752c252. - * However, this buggy behavior is kept under particular circumstances - * to accommodate deployed binaries of the "tclcompiler" program - * <http://sourceforge.net/projects/tclpro/> that crash if the bug is - * fixed. + * [***] This is wrong. See Tcl Bug a16752c252. + * However, this buggy behavior is kept under particular + * circumstances to accommodate deployed binaries of the + * "tclcompiler" program. http://sourceforge.net/projects/tclpro/ + * that crash if the bug is fixed. */ if (cmdPtr->objProc == TclInvokeStringCommand @@ -2844,47 +2082,25 @@ TclCreateObjCommandInNs( cmdPtr->flags |= CMD_REDEF_IN_PROGRESS; } - /* - * Make sure namespace doesn't get deallocated. - */ - - cmdPtr->nsPtr->refCount++; - Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); - nsPtr = (Namespace *) TclEnsureNamespace(interp, - (Tcl_Namespace *) cmdPtr->nsPtr); - TclNsDecrRefCount(cmdPtr->nsPtr); if (cmdPtr->flags & CMD_REDEF_IN_PROGRESS) { oldRefPtr = cmdPtr->importRefPtr; cmdPtr->importRefPtr = NULL; } TclCleanupCommandMacro(cmdPtr); - deleted = 1; - } - 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). - */ - - ckfree(Tcl_GetHashValue(hPtr)); - } - if (!deleted) { - /* - * Command resolvers (per-interp, per-namespace) might have resolved - * to a command for the given namespace scope with this command not - * being registered with the namespace's command table. During BC - * compilation, the so-resolved command turns into a CmdName literal. - * Without invalidating a possible CmdName literal here explicitly, - * such literals keep being reused while pointing to overhauled - * commands. - */ - - TclInvalidateCmdLiteral(interp, cmdName, nsPtr); + 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). + */ + 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 @@ -2892,9 +2108,8 @@ TclCreateObjCommandInNs( */ TclInvalidateNsCmdLookup(nsPtr); - TclInvalidateNsPath(nsPtr); } - cmdPtr = (Command *)ckalloc(sizeof(Command)); + cmdPtr = (Command *) ckalloc(sizeof(Command)); Tcl_SetHashValue(hPtr, cmdPtr); cmdPtr->hPtr = hPtr; cmdPtr->nsPtr = nsPtr; @@ -2910,7 +2125,6 @@ TclCreateObjCommandInNs( cmdPtr->flags = 0; cmdPtr->importRefPtr = NULL; cmdPtr->tracePtr = NULL; - cmdPtr->nreProc = NULL; /* * Plug in any existing import references found above. Be sure to update @@ -2920,11 +2134,8 @@ TclCreateObjCommandInNs( if (oldRefPtr != NULL) { cmdPtr->importRefPtr = oldRefPtr; while (oldRefPtr != NULL) { - Command *refCmdPtr = oldRefPtr->importedCmdPtr; - - dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; - cmdPtr->refCount++; - TclCleanupCommandMacro(dataPtr->realCmdPtr); + refCmdPtr = oldRefPtr->importedCmdPtr; + dataPtr = refCmdPtr->objClientData; dataPtr->realCmdPtr = cmdPtr; oldRefPtr = oldRefPtr->nextPtr; } @@ -2964,18 +2175,18 @@ TclCreateObjCommandInNs( int TclInvokeStringCommand( - void *clientData, /* Points to command's Command structure. */ + ClientData clientData, /* Points to command's Command structure. */ Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ + register int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { - Command *cmdPtr = (Command *)clientData; + Command *cmdPtr = clientData; int i, result; const char **argv = (const char **) - TclStackAlloc(interp, (objc + 1) * sizeof(char *)); + TclStackAlloc(interp, (unsigned)(objc + 1) * sizeof(char *)); - for (i = 0; i < objc; i++) { - argv[i] = TclGetString(objv[i]); + for (i = 0; i < objc; i++) { + argv[i] = Tcl_GetString(objv[i]); } argv[objc] = 0; @@ -2983,7 +2194,7 @@ TclInvokeStringCommand( * Invoke the command's string-based Tcl_CmdProc. */ - result = cmdPtr->proc(cmdPtr->clientData, interp, objc, argv); + result = (*cmdPtr->proc)(cmdPtr->clientData, interp, objc, argv); TclStackFree(interp, (void *) argv); return result; @@ -3001,7 +2212,7 @@ TclInvokeStringCommand( * in the Command structure. * * Results: - * A standard Tcl result value. + * A standard Tcl string result value. * * Side effects: * Besides those side effects of the called Tcl_ObjCmdProc, @@ -3012,18 +2223,18 @@ TclInvokeStringCommand( int TclInvokeObjectCommand( - void *clientData, /* Points to command's Command structure. */ + ClientData clientData, /* Points to command's Command structure. */ Tcl_Interp *interp, /* Current interpreter. */ int argc, /* Number of arguments. */ - const char **argv) /* Argument strings. */ + register const char **argv) /* Argument strings. */ { - Command *cmdPtr = ( Command *) clientData; + Command *cmdPtr = (Command *) clientData; Tcl_Obj *objPtr; int i, length, result; Tcl_Obj **objv = (Tcl_Obj **) - TclStackAlloc(interp, (argc * sizeof(Tcl_Obj *))); + TclStackAlloc(interp, (unsigned)(argc * sizeof(Tcl_Obj *))); - for (i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { length = strlen(argv[i]); TclNewStringObj(objPtr, argv[i], length); Tcl_IncrRefCount(objPtr); @@ -3034,12 +2245,7 @@ TclInvokeObjectCommand( * Invoke the command's object-based Tcl_ObjCmdProc. */ - if (cmdPtr->objProc != NULL) { - result = cmdPtr->objProc(cmdPtr->objClientData, interp, argc, objv); - } else { - result = Tcl_NRCallObjProc(interp, cmdPtr->nreProc, - cmdPtr->objClientData, argc, objv); - } + result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, argc, objv); /* * Move the interpreter's object result to the string result, then reset @@ -3053,7 +2259,7 @@ TclInvokeObjectCommand( * free the objv array if malloc'ed storage was used. */ - for (i = 0; i < argc; i++) { + for (i = 0; i < argc; i++) { objPtr = objv[i]; Tcl_DecrRefCount(objPtr); } @@ -3109,13 +2315,15 @@ TclRenameCommand( cmd = Tcl_FindCommand(interp, oldName, NULL, /*flags*/ 0); cmdPtr = (Command *) cmd; if (cmdPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't %s \"%s\": command doesn't exist", + Tcl_AppendResult(interp, "can't ", ((newName == NULL)||(*newName == '\0'))? "delete":"rename", - oldName)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", oldName, (void *)NULL); + " \"", oldName, "\": command doesn't exist", NULL); return TCL_ERROR; } + cmdNsPtr = cmdPtr->nsPtr; + oldFullName = Tcl_NewObj(); + Tcl_IncrRefCount(oldFullName); + Tcl_GetCommandFullName(interp, cmd, oldFullName); /* * If the new command name is NULL or empty, delete the command. Do this @@ -3124,42 +2332,35 @@ TclRenameCommand( if ((newName == NULL) || (*newName == '\0')) { Tcl_DeleteCommandFromToken(interp, cmd); - return TCL_OK; + result = TCL_OK; + goto done; } - cmdNsPtr = cmdPtr->nsPtr; - TclNewObj(oldFullName); - Tcl_IncrRefCount(oldFullName); - Tcl_GetCommandFullName(interp, cmd, oldFullName); - /* * 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_CreateObjCommand would. + * create the containing namespaces just like Tcl_CreateCommand would. */ TclGetNamespaceForQualName(interp, newName, NULL, TCL_CREATE_NS_IF_UNKNOWN, &newNsPtr, &dummy1, &dummy2, &newTail); if ((newNsPtr == NULL) || (newTail == NULL)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't rename to \"%s\": bad command name", newName)); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", (void *)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_SetObjResult(interp, Tcl_ObjPrintf( - "can't rename to \"%s\": command already exists", newName)); - Tcl_SetErrorCode(interp, "TCL", "OPERATION", "RENAME", - "TARGET_EXISTS", (void *)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). + * Tcl_HideCommand() code too (until the common parts are extracted out). * - dl */ @@ -3200,17 +2401,6 @@ TclRenameCommand( TclInvalidateNsCmdLookup(cmdPtr->nsPtr); /* - * Command resolvers (per-interp, per-namespace) might have resolved to a - * command for the given namespace scope with this command not being - * registered with the namespace's command table. During BC compilation, - * the so-resolved command turns into a CmdName literal. Without - * invalidating a possible CmdName literal here explicitly, such literals - * keep being reused while pointing to overhauled commands. - */ - - TclInvalidateCmdLiteral(interp, newTail, 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 @@ -3223,13 +2413,13 @@ TclRenameCommand( */ Tcl_DStringInit(&newFullName); - Tcl_DStringAppend(&newFullName, newNsPtr->fullName, TCL_INDEX_NONE); + Tcl_DStringAppend(&newFullName, newNsPtr->fullName, -1); if (newNsPtr != iPtr->globalNsPtr) { - TclDStringAppendLiteral(&newFullName, "::"); + Tcl_DStringAppend(&newFullName, "::", 2); } - Tcl_DStringAppend(&newFullName, newTail, TCL_INDEX_NONE); + Tcl_DStringAppend(&newFullName, newTail, -1); cmdPtr->refCount++; - CallCommandTraces(iPtr, cmdPtr, TclGetString(oldFullName), + CallCommandTraces(iPtr, cmdPtr, Tcl_GetString(oldFullName), Tcl_DStringValue(&newFullName), TCL_TRACE_RENAME); Tcl_DStringFree(&newFullName); @@ -3329,7 +2519,7 @@ Tcl_SetCommandInfoFromToken( { Command *cmdPtr; /* Internal representation of the command */ - if (cmd == NULL) { + if (cmd == (Tcl_Command) NULL) { return 0; } @@ -3343,12 +2533,8 @@ Tcl_SetCommandInfoFromToken( if (infoPtr->objProc == NULL) { cmdPtr->objProc = TclInvokeStringCommand; cmdPtr->objClientData = cmdPtr; - cmdPtr->nreProc = NULL; } else { - if (infoPtr->objProc != cmdPtr->objProc) { - cmdPtr->nreProc = NULL; - cmdPtr->objProc = infoPtr->objProc; - } + cmdPtr->objProc = infoPtr->objProc; cmdPtr->objClientData = infoPtr->objClientData; } cmdPtr->deleteProc = infoPtr->deleteProc; @@ -3413,7 +2599,7 @@ Tcl_GetCommandInfoFromToken( { Command *cmdPtr; /* Internal representation of the command */ - if (cmd == NULL) { + if (cmd == (Tcl_Command) NULL) { return 0; } @@ -3432,6 +2618,7 @@ Tcl_GetCommandInfoFromToken( infoPtr->deleteProc = cmdPtr->deleteProc; infoPtr->deleteData = cmdPtr->deleteData; infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr; + return 1; } @@ -3440,7 +2627,7 @@ Tcl_GetCommandInfoFromToken( * * Tcl_GetCommandName -- * - * Given a token returned by Tcl_CreateObjCommand, this function returns the + * Given a token returned by Tcl_CreateCommand, this function returns the * current name of the command (which may have changed due to renaming). * * Results: @@ -3454,9 +2641,9 @@ Tcl_GetCommandInfoFromToken( const char * Tcl_GetCommandName( - TCL_UNUSED(Tcl_Interp *), + Tcl_Interp *interp, /* Interpreter containing the command. */ Tcl_Command command) /* Token for command returned by a previous - * call to Tcl_CreateObjCommand. The command must + * call to Tcl_CreateCommand. The command must * not have been deleted. */ { Command *cmdPtr = (Command *) command; @@ -3471,7 +2658,7 @@ Tcl_GetCommandName( return ""; } - return (const char *)Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); + return Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); } /* @@ -3479,7 +2666,7 @@ Tcl_GetCommandName( * * Tcl_GetCommandFullName -- * - * Given a token returned by, e.g., Tcl_CreateObjCommand or Tcl_FindCommand, + * 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. @@ -3498,14 +2685,14 @@ void Tcl_GetCommandFullName( Tcl_Interp *interp, /* Interpreter containing the command. */ Tcl_Command command, /* Token for command returned by a previous - * call to Tcl_CreateObjCommand. The command must + * 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. */ { Interp *iPtr = (Interp *) interp; - Command *cmdPtr = (Command *) command; + register Command *cmdPtr = (Command *) command; char *name; /* @@ -3513,16 +2700,16 @@ Tcl_GetCommandFullName( * separator, and the command name. */ - if ((cmdPtr != NULL) && TclRoutineHasName(cmdPtr)) { + if (cmdPtr != NULL) { if (cmdPtr->nsPtr != NULL) { - Tcl_AppendToObj(objPtr, cmdPtr->nsPtr->fullName, TCL_INDEX_NONE); + Tcl_AppendToObj(objPtr, cmdPtr->nsPtr->fullName, -1); if (cmdPtr->nsPtr != iPtr->globalNsPtr) { Tcl_AppendToObj(objPtr, "::", 2); } } if (cmdPtr->hPtr != NULL) { - name = (char *)Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); - Tcl_AppendToObj(objPtr, name, TCL_INDEX_NONE); + name = Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr); + Tcl_AppendToObj(objPtr, name, -1); } } } @@ -3557,7 +2744,7 @@ Tcl_DeleteCommand( */ cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); - if (cmd == NULL) { + if (cmd == (Tcl_Command) NULL) { return -1; } return Tcl_DeleteCommandFromToken(interp, cmd); @@ -3595,6 +2782,13 @@ Tcl_DeleteCommandFromToken( Tcl_Command importCmd; /* + * 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 @@ -3603,7 +2797,7 @@ Tcl_DeleteCommandFromToken( * and skip nested deletes. */ - if (cmdPtr->flags & CMD_DYING) { + 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 @@ -3616,37 +2810,26 @@ Tcl_DeleteCommandFromToken( Tcl_DeleteHashEntry(cmdPtr->hPtr); cmdPtr->hPtr = NULL; } - - /* - * Bump the command epoch counter. This will invalidate all cached - * references that point to this command. - */ - - cmdPtr->cmdEpoch++; - 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 themselves. This flag + * 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_DYING; + cmdPtr->flags |= CMD_IS_DELETED; /* - * Call each functions and then delete the trace. + * Call trace functions for the command being deleted. Then delete its + * traces. */ - cmdPtr->nsPtr->refCount++; - if (cmdPtr->tracePtr != NULL) { CommandTrace *tracePtr; - /* CallCommandTraces() does not cmdPtr, that's - * done just before Tcl_DeleteCommandFromToken() returns */ CallCommandTraces(iPtr,cmdPtr,NULL,NULL,TCL_TRACE_DELETE); /* @@ -3656,9 +2839,8 @@ Tcl_DeleteCommandFromToken( tracePtr = cmdPtr->tracePtr; while (tracePtr != NULL) { CommandTrace *nextPtr = tracePtr->nextPtr; - - if (tracePtr->refCount-- <= 1) { - ckfree(tracePtr); + if ((--tracePtr->refCount) <= 0) { + ckfree((char *) tracePtr); } tracePtr = nextPtr; } @@ -3666,13 +2848,12 @@ Tcl_DeleteCommandFromToken( } /* - * The list of commands exported from the namespace might have changed. + * 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); - TclNsDecrRefCount(cmdPtr->nsPtr); /* * If the command being deleted has a compile function, increment the @@ -3687,36 +2868,39 @@ Tcl_DeleteCommandFromToken( iPtr->compileEpoch++; } - if (!(cmdPtr->flags & CMD_REDEF_IN_PROGRESS)) { - /* - * Delete any imports of this routine before deleting this routine itself. - * See issue 688fcc7082fa. - */ - for (refPtr = cmdPtr->importRefPtr; refPtr != NULL; - refPtr = nextRefPtr) { - nextRefPtr = refPtr->nextPtr; - importCmd = (Tcl_Command) refPtr->importedCmdPtr; - Tcl_DeleteCommandFromToken(interp, importCmd); - } - } - if (cmdPtr->deleteProc != NULL) { /* * Delete the command's client data. If this was an imported command * created when a command was imported into a namespace, this client * 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() + * 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); + (*cmdPtr->deleteProc)(cmdPtr->deleteData); + } + + /* + * If this command was imported into other namespaces, then imported + * commands were created that refer back to this command. Delete these + * imported commands now. + */ + if (!(cmdPtr->flags & CMD_REDEF_IN_PROGRESS)) { + for (refPtr = cmdPtr->importRefPtr; refPtr != NULL; + refPtr = nextRefPtr) { + nextRefPtr = refPtr->nextPtr; + importCmd = (Tcl_Command) refPtr->importedCmdPtr; + Tcl_DeleteCommandFromToken(interp, importCmd); + } } /* @@ -3729,20 +2913,14 @@ Tcl_DeleteCommandFromToken( if (cmdPtr->hPtr != NULL) { Tcl_DeleteHashEntry(cmdPtr->hPtr); cmdPtr->hPtr = NULL; - - /* - * Bump the command epoch counter. This will invalidate all cached - * references that point to this command. - */ - - cmdPtr->cmdEpoch++; } /* - * A number of tests for particular kinds of commands are done by checking - * whether the objProc field holds a known value. Set the field to NULL so - * that such tests won't have false positives when applied to deleted - * commands. + * 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. */ cmdPtr->objProc = NULL; @@ -3752,32 +2930,14 @@ Tcl_DeleteCommandFromToken( * 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 - * TclNRExecuteByteCode looks up the command in the command hashtable). + * CmdName Command reference is found to be invalid and TclExecuteByteCode + * looks up the command in the command hashtable). */ - cmdPtr->flags |= CMD_DEAD; TclCleanupCommandMacro(cmdPtr); return 0; } -/* - *---------------------------------------------------------------------- - * - * CallCommandTraces -- - * - * Abstraction of the code to call traces on a command. - * - * Results: - * Currently always NULL. - * - * Side effects: - * Anything; this may recursively evaluate scripts and code exists to do - * just that. - * - *---------------------------------------------------------------------- - */ - static char * CallCommandTraces( Interp *iPtr, /* Interpreter containing command. */ @@ -3790,7 +2950,7 @@ CallCommandTraces( * trigger, either TCL_TRACE_DELETE or * TCL_TRACE_RENAME. */ { - CommandTrace *tracePtr; + register CommandTrace *tracePtr; ActiveCommandTrace active; char *result; Tcl_Obj *oldNamePtr = NULL; @@ -3801,7 +2961,7 @@ CallCommandTraces( * 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_DYING) and returns immediately when a + * (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 @@ -3816,6 +2976,7 @@ CallCommandTraces( } } cmdPtr->flags |= CMD_TRACE_ACTIVE; + cmdPtr->refCount++; result = NULL; active.nextPtr = iPtr->activeCmdTracePtr; @@ -3847,11 +3008,11 @@ CallCommandTraces( if (state == NULL) { state = Tcl_SaveInterpState((Tcl_Interp *) iPtr, TCL_OK); } - tracePtr->traceProc(tracePtr->clientData, (Tcl_Interp *) iPtr, - oldName, newName, flags); + (*tracePtr->traceProc)(tracePtr->clientData, + (Tcl_Interp *) iPtr, oldName, newName, flags); cmdPtr->flags &= ~tracePtr->flags; - if (tracePtr->refCount-- <= 1) { - ckfree(tracePtr); + if ((--tracePtr->refCount) <= 0) { + ckfree((char *) tracePtr); } } @@ -3873,86 +3034,39 @@ CallCommandTraces( */ cmdPtr->flags &= ~CMD_TRACE_ACTIVE; + cmdPtr->refCount--; iPtr->activeCmdTracePtr = active.nextPtr; Tcl_Release(iPtr); return result; } - + /* *---------------------------------------------------------------------- * - * CancelEvalProc -- - * - * Marks this interpreter as being canceled. This causes current - * executions to be unwound as the interpreter enters a state where it - * refuses to execute more commands or handle [catch] or [try], yet the - * interpreter is still able to execute further commands after the - * cancelation is cleared (unlike if it is deleted). + * GetCommandSource -- * - * Results: - * The value given for the code argument. - * - * Side effects: - * Transfers a message from the cancellation message to the interpreter. + * 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 int -CancelEvalProc( - void *clientData, /* Interp to cancel the script in progress. */ - TCL_UNUSED(Tcl_Interp *), - int code) /* Current return code from command. */ +static Tcl_Obj * +GetCommandSource( + Interp *iPtr, + const char *command, + int numChars, + int objc, + Tcl_Obj *const objv[]) { - CancelInfo *cancelInfo = (CancelInfo *)clientData; - Interp *iPtr; - - if (cancelInfo != NULL) { - Tcl_MutexLock(&cancelLock); - iPtr = (Interp *) cancelInfo->interp; - - if (iPtr != NULL) { - /* - * Setting the CANCELED flag will cause the script in progress to - * be canceled as soon as possible. The core honors this flag at - * all the necessary places to ensure script cancellation is - * responsive. Extensions can check for this flag by calling - * Tcl_Canceled and checking if TCL_ERROR is returned or they can - * choose to ignore the script cancellation flag and the - * associated functionality altogether. Currently, the only other - * flag we care about here is the TCL_CANCEL_UNWIND flag (from - * Tcl_CancelEval). We do not want to simply combine all the flags - * from original Tcl_CancelEval call with the interp flags here - * just in case the caller passed flags that might cause behaviour - * unrelated to script cancellation. - */ - - TclSetCancelFlags(iPtr, cancelInfo->flags | CANCELED); - - /* - * Now, we must set the script cancellation flags on all the child - * interpreters belonging to this one. - */ - - TclSetChildCancelFlags((Tcl_Interp *) iPtr, - cancelInfo->flags | CANCELED, 0); - - /* - * Create the result object now so that Tcl_Canceled can avoid - * locking the cancelLock mutex. - */ - - if (cancelInfo->result != NULL) { - Tcl_SetStringObj(iPtr->asyncCancelMsg, cancelInfo->result, - cancelInfo->length); - } else { - Tcl_SetObjLength(iPtr->asyncCancelMsg, 0); - } - } - Tcl_MutexUnlock(&cancelLock); + if (!command) { + return Tcl_NewListObj(objc, objv); } - - return code; + if (command == (char *) -1) { + command = TclGetSrcInfoForCmd(iPtr, &numChars); + } + return Tcl_NewStringObj(command, numChars); } /* @@ -3978,11 +3092,12 @@ CancelEvalProc( void TclCleanupCommand( - Command *cmdPtr) /* Points to the Command structure to + register Command *cmdPtr) /* Points to the Command structure to * be freed. */ { - if (cmdPtr->refCount-- <= 1) { - ckfree(cmdPtr); + cmdPtr->refCount--; + if (cmdPtr->refCount <= 0) { + ckfree((char *) cmdPtr); } } @@ -4002,40 +3117,39 @@ TclCleanupCommand( * 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, - * redefining a non-builtin function will force existing code to be + * redefioning a non-builtin function will force existing code to be * invalidated if the number of arguments has changed. * *---------------------------------------------------------------------- */ -#if !defined(TCL_NO_DEPRECATED) void Tcl_CreateMathFunc( Tcl_Interp *interp, /* Interpreter in which function is to be * available. */ const char *name, /* Name of function (e.g. "sin"). */ - int numArgs, /* Number of arguments required by + 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. */ - void *clientData) /* Additional value to pass to the + ClientData clientData) /* Additional value to pass to the * function. */ { Tcl_DString bigName; - OldMathFuncData *data = (OldMathFuncData *)ckalloc(sizeof(OldMathFuncData)); + OldMathFuncData *data = (OldMathFuncData *) + ckalloc(sizeof(OldMathFuncData)); data->proc = proc; data->numArgs = numArgs; - data->argTypes = (Tcl_ValueType *)ckalloc(numArgs * sizeof(Tcl_ValueType)); - if ((numArgs > 0) && (argTypes != NULL)) { - memcpy(data->argTypes, argTypes, numArgs * sizeof(Tcl_ValueType)); - } + data->argTypes = (Tcl_ValueType *) + ckalloc(numArgs * sizeof(Tcl_ValueType)); + memcpy(data->argTypes, argTypes, numArgs * sizeof(Tcl_ValueType)); data->clientData = clientData; Tcl_DStringInit(&bigName); - TclDStringAppendLiteral(&bigName, "::tcl::mathfunc::"); + Tcl_DStringAppend(&bigName, "::tcl::mathfunc::", -1); Tcl_DStringAppend(&bigName, name, -1); Tcl_CreateObjCommand(interp, Tcl_DStringValue(&bigName), @@ -4061,14 +3175,14 @@ Tcl_CreateMathFunc( static int OldMathFuncProc( - void *clientData, /* Pointer to OldMathFuncData describing the + 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 = (OldMathFuncData *)clientData; + OldMathFuncData *dataPtr = clientData; Tcl_Value funcResult, *args; int result; int j, k; @@ -4087,20 +3201,16 @@ OldMathFuncProc( * Convert arguments from Tcl_Obj's to Tcl_Value's. */ - args = (Tcl_Value *)ckalloc(dataPtr->numArgs * sizeof(Tcl_Value)); + args = (Tcl_Value *) ckalloc(dataPtr->numArgs * sizeof(Tcl_Value)); for (j = 1, k = 0; j < objc; ++j, ++k) { - /* TODO: Convert to Tcl_GetNumberFromObj? */ + + /* TODO: Convert to TclGetNumberFromObj() ? */ valuePtr = objv[j]; result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d); #ifdef ACCEPT_NAN - if (result != TCL_OK) { - const Tcl_ObjInternalRep *irPtr - = TclFetchInternalRep(valuePtr, &tclDoubleType); - - if (irPtr) { - d = irPtr->doubleValue; - result = TCL_OK; - } + if ((result != TCL_OK) && (valuePtr->typePtr == &tclDoubleType)) { + d = valuePtr->internalRep.doubleValue; + result = TCL_OK; } #endif if (result != TCL_OK) { @@ -4109,10 +3219,9 @@ OldMathFuncProc( */ Tcl_SetObjResult(interp, Tcl_NewStringObj( - "argument to math function didn't have numeric value", - -1)); - TclCheckBadOctal(interp, TclGetString(valuePtr)); - ckfree(args); + "argument to math function didn't have numeric value",-1)); + TclCheckBadOctal(interp, Tcl_GetString(valuePtr)); + ckfree((char *)args); return TCL_ERROR; } @@ -4126,12 +3235,12 @@ OldMathFuncProc( args[k].type = dataPtr->argTypes[k]; switch (args[k].type) { case TCL_EITHER: - if (Tcl_GetLongFromObj(NULL, valuePtr, &args[k].intValue) + if (Tcl_GetLongFromObj(NULL, valuePtr, &(args[k].intValue)) == TCL_OK) { args[k].type = TCL_INT; break; } - if (TclGetWideIntFromObj(interp, valuePtr, &args[k].wideValue) + if (Tcl_GetWideIntFromObj(interp, valuePtr, &(args[k].wideValue)) == TCL_OK) { args[k].type = TCL_WIDE_INT; break; @@ -4143,21 +3252,21 @@ OldMathFuncProc( args[k].doubleValue = d; break; case TCL_INT: - if (ExprIntFunc(NULL, interp, 2, &objv[j-1]) != TCL_OK) { - ckfree(args); + 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_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(args); + if (ExprWideFunc(NULL, interp, 2, &(objv[j-1])) != TCL_OK) { + ckfree((char *)args); return TCL_ERROR; } valuePtr = Tcl_GetObjResult(interp); - TclGetWideIntFromObj(NULL, valuePtr, &args[k].wideValue); + Tcl_GetWideIntFromObj(NULL, valuePtr, &(args[k].wideValue)); Tcl_ResetResult(interp); break; } @@ -4168,8 +3277,8 @@ OldMathFuncProc( */ errno = 0; - result = dataPtr->proc(dataPtr->clientData, interp, args, &funcResult); - ckfree(args); + result = (*dataPtr->proc)(dataPtr->clientData, interp, args, &funcResult); + ckfree((char *)args); if (result != TCL_OK) { return result; } @@ -4179,9 +3288,9 @@ OldMathFuncProc( */ if (funcResult.type == TCL_INT) { - TclNewIntObj(valuePtr, funcResult.intValue); + TclNewLongObj(valuePtr, funcResult.intValue); } else if (funcResult.type == TCL_WIDE_INT) { - TclNewIntObj(valuePtr, funcResult.wideValue); + valuePtr = Tcl_NewWideIntObj(funcResult.wideValue); } else { return CheckDoubleResult(interp, funcResult.doubleValue); } @@ -4208,12 +3317,12 @@ OldMathFuncProc( static void OldMathFuncDeleteProc( - void *clientData) + ClientData clientData) { - OldMathFuncData *dataPtr = (OldMathFuncData *)clientData; + OldMathFuncData *dataPtr = clientData; - ckfree(dataPtr->argTypes); - ckfree(dataPtr); + ckfree((void *) dataPtr->argTypes); + ckfree((void *) dataPtr); } /* @@ -4247,7 +3356,7 @@ Tcl_GetMathFuncInfo( int *numArgsPtr, Tcl_ValueType **argTypesPtr, Tcl_MathProc **procPtr, - void **clientDataPtr) + ClientData *clientDataPtr) { Tcl_Obj *cmdNameObj; Command *cmdPtr; @@ -4267,9 +3376,12 @@ Tcl_GetMathFuncInfo( */ if (cmdPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unknown math function \"%s\"", name)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "MATHFUNC", name, (void *)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; @@ -4283,7 +3395,7 @@ Tcl_GetMathFuncInfo( */ if (cmdPtr->objProc == &OldMathFuncProc) { - OldMathFuncData *dataPtr = (OldMathFuncData *)cmdPtr->clientData; + OldMathFuncData *dataPtr = cmdPtr->clientData; *procPtr = dataPtr->proc; *numArgsPtr = dataPtr->numArgs; @@ -4340,14 +3452,13 @@ Tcl_ListMathFuncs( if (TCL_OK == Tcl_EvalObjEx(interp, script, 0)) { result = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); } else { - TclNewObj(result); + result = Tcl_NewObj(); } Tcl_DecrRefCount(script); Tcl_RestoreInterpState(interp, state); return result; } -#endif /* !defined(TCL_NO_DEPRECATED) */ /* *---------------------------------------------------------------------- @@ -4371,11 +3482,14 @@ int TclInterpReady( Tcl_Interp *interp) { - Interp *iPtr = (Interp *) interp; +#if !defined(TCL_NO_STACK_CHECK) + int localInt; /* used for checking the stack */ +#endif + register Interp *iPtr = (Interp *) interp; /* - * Reset the interpreter's result 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); @@ -4385,23 +3499,11 @@ TclInterpReady( */ if (iPtr->flags & DELETED) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "attempt to call eval in deleted interpreter", TCL_INDEX_NONE)); + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, + "attempt to call eval in deleted interpreter", NULL); Tcl_SetErrorCode(interp, "TCL", "IDELETE", - "attempt to call eval in deleted interpreter", (void *)NULL); - return TCL_ERROR; - } - - if (iPtr->execEnvPtr->rewind) { - return TCL_ERROR; - } - - /* - * Make sure the script being evaluated (if any) has not been canceled. - */ - - if (TclCanceled(iPtr) && - (TCL_OK != Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG))) { + "attempt to call eval in deleted interpreter", NULL); return TCL_ERROR; } @@ -4410,366 +3512,81 @@ TclInterpReady( * probably because of an infinite loop somewhere. */ - if ((iPtr->numLevels <= iPtr->maxNestingDepth)) { + if (((iPtr->numLevels) <= iPtr->maxNestingDepth) + && CheckCStack(iPtr, &localInt)) { return TCL_OK; } - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "too many nested evaluations (infinite loop?)", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "LIMIT", "STACK", (void *)NULL); - return TCL_ERROR; -} - -/* - *---------------------------------------------------------------------- - * - * TclResetCancellation -- - * - * Reset the script cancellation flags if the nesting level - * (iPtr->numLevels) for the interp is zero or argument force is - * non-zero. - * - * Results: - * A standard Tcl result. - * - * Side effects: - * The script cancellation flags for the interp may be reset. - * - *---------------------------------------------------------------------- - */ - -int -TclResetCancellation( - Tcl_Interp *interp, - int force) -{ - Interp *iPtr = (Interp *) interp; - - if (iPtr == NULL) { - return TCL_ERROR; - } - - if (force || (iPtr->numLevels == 0)) { - TclUnsetCancelFlags(iPtr); - } - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_Canceled -- - * - * Check if the script in progress has been canceled, i.e., - * Tcl_CancelEval was called for this interpreter or any of its parent - * interpreters. - * - * Results: - * The return value is TCL_OK if the script evaluation has not been - * canceled, TCL_ERROR otherwise. - * - * If "flags" contains TCL_LEAVE_ERR_MSG, an error message is returned in - * the interpreter's result object. Otherwise, the interpreter's result - * object is left unchanged. If "flags" contains TCL_CANCEL_UNWIND, - * TCL_ERROR will only be returned if the script evaluation is being - * completely unwound. - * - * Side effects: - * The CANCELED flag for the interp will be reset if it is set. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_Canceled( - Tcl_Interp *interp, - int flags) -{ - Interp *iPtr = (Interp *) interp; - - /* - * Has the current script in progress for this interpreter been canceled - * or is the stack being unwound due to the previous script cancellation? - */ - - if (!TclCanceled(iPtr)) { - return TCL_OK; - } - - /* - * The CANCELED flag is a one-shot flag that is reset immediately upon - * being detected; however, if the TCL_CANCEL_UNWIND flag is set we will - * continue to report that the script in progress has been canceled - * thereby allowing the evaluation stack for the interp to be fully - * unwound. - */ - - iPtr->flags &= ~CANCELED; - - /* - * The CANCELED flag was detected and reset; however, if the caller - * specified the TCL_CANCEL_UNWIND flag, we only return TCL_ERROR - * (indicating that the script in progress has been canceled) if the - * evaluation stack for the interp is being fully unwound. - */ - - if ((flags & TCL_CANCEL_UNWIND) && !(iPtr->flags & TCL_CANCEL_UNWIND)) { - return TCL_OK; - } - - /* - * If the TCL_LEAVE_ERR_MSG flags bit is set, place an error in the - * interp's result; otherwise, we leave it alone. - */ - - if (flags & TCL_LEAVE_ERR_MSG) { - const char *id, *message = NULL; - Tcl_Size length; - - /* - * Setup errorCode variables so that we can differentiate between - * being canceled and unwound. - */ - - if (iPtr->asyncCancelMsg != NULL) { - message = TclGetStringFromObj(iPtr->asyncCancelMsg, &length); - } else { - length = 0; - } - - if (iPtr->flags & TCL_CANCEL_UNWIND) { - id = "IUNWIND"; - if (length == 0) { - message = "eval unwound"; - } - } else { - id = "ICANCEL"; - if (length == 0) { - message = "eval canceled"; - } - } - - Tcl_SetObjResult(interp, Tcl_NewStringObj(message, TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "CANCEL", id, message, (void *)NULL); - } - - /* - * Return TCL_ERROR to the caller (not necessarily just the Tcl core - * itself) that indicates further processing of the script or command in - * progress should halt gracefully and as soon as possible. - */ - - return TCL_ERROR; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_CancelEval -- - * - * This function schedules the cancellation of the current script in the - * given interpreter. - * - * Results: - * The return value is a standard Tcl completion code such as TCL_OK or - * TCL_ERROR. Since the interp may belong to a different thread, no error - * message can be left in the interp's result. - * - * Side effects: - * The script in progress in the specified interpreter will be canceled - * with TCL_ERROR after asynchronous handlers are invoked at the next - * Tcl_Canceled check. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_CancelEval( - Tcl_Interp *interp, /* Interpreter in which to cancel the - * script. */ - Tcl_Obj *resultObjPtr, /* The script cancellation error message or - * NULL for a default error message. */ - void *clientData, /* Passed to CancelEvalProc. */ - int flags) /* Collection of OR-ed bits that control - * the cancellation of the script. Only - * TCL_CANCEL_UNWIND is currently - * supported. */ -{ - Tcl_HashEntry *hPtr; - CancelInfo *cancelInfo; - int code = TCL_ERROR; - const char *result; - - if (interp == NULL) { - return TCL_ERROR; - } - - Tcl_MutexLock(&cancelLock); - if (cancelTableInitialized != 1) { - /* - * No CancelInfo hash table (Tcl_CreateInterp has never been called?) - */ - - goto done; - } - hPtr = Tcl_FindHashEntry(&cancelTable, interp); - if (hPtr == NULL) { - /* - * No CancelInfo record for this interpreter. - */ - - goto done; - } - cancelInfo = (CancelInfo *)Tcl_GetHashValue(hPtr); - - /* - * Populate information needed by the interpreter thread to fulfill the - * cancellation request. Currently, clientData is ignored. If the - * TCL_CANCEL_UNWIND flags bit is set, the script in progress is not - * allowed to catch the script cancellation because the evaluation stack - * for the interp is completely unwound. - */ - - if (resultObjPtr != NULL) { - result = TclGetStringFromObj(resultObjPtr, &cancelInfo->length); - cancelInfo->result = (char *)ckrealloc(cancelInfo->result,cancelInfo->length); - memcpy(cancelInfo->result, result, cancelInfo->length); - TclDecrRefCount(resultObjPtr); /* Discard their result object. */ + if (!CheckCStack(iPtr, &localInt)) { + Tcl_AppendResult(interp, + "out of stack space (infinite loop?)", NULL); } else { - cancelInfo->result = NULL; - cancelInfo->length = 0; + Tcl_AppendResult(interp, + "too many nested evaluations (infinite loop?)", NULL); } - cancelInfo->clientData = clientData; - cancelInfo->flags = flags; - Tcl_AsyncMark(cancelInfo->async); - code = TCL_OK; - - done: - Tcl_MutexUnlock(&cancelLock); - return code; -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_InterpActive -- - * - * Returns non-zero if the specified interpreter is in use, i.e. if there - * is an evaluation currently active in the interpreter. - * - * Results: - * See above. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_InterpActive( - Tcl_Interp *interp) -{ - return ((Interp *) interp)->numLevels > 0; + return TCL_ERROR; } /* *---------------------------------------------------------------------- * - * Tcl_EvalObjv -- + * TclEvalObjvInternal * * This function evaluates a Tcl command that has already been parsed - * into words, with one Tcl_Obj holding each word. + * 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. + * 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: - * Always pushes a callback. Other side effects depend on the command. + * Depends on the command. * *---------------------------------------------------------------------- */ int -Tcl_EvalObjv( +TclEvalObjvInternal( Tcl_Interp *interp, /* Interpreter in which to evaluate the * command. Also used for error reporting. */ - Tcl_Size objc, /* Number of words in command. */ + 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. 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 - * TCL_EVAL_GLOBAL, TCL_EVAL_INVOKE and - * TCL_EVAL_NOERR are currently supported. */ -{ - int result; - NRE_callback *rootPtr = TOP_CB(interp); - - result = TclNREvalObjv(interp, objc, objv, flags, NULL); - return TclNRRunCallbacks(interp, result, rootPtr); -} - -int -TclNREvalObjv( - Tcl_Interp *interp, /* Interpreter in which to evaluate the - * command. Also used for error reporting. */ - Tcl_Size 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, TCL_EVAL_INVOKE and - * TCL_EVAL_NOERR are currently supported. */ - Command *cmdPtr) /* NULL if the Command is to be looked up - * here, otherwise the pointer to the - * requested Command struct to be invoked. */ -{ - Interp *iPtr = (Interp *) interp; - - /* - * data[1] stores a marker for use by tailcalls; it will be set to 1 by - * command redirectors (imports, alias, ensembles) so that tailcall skips - * this callback (that marks the end of the target command) and goes back - * to the end of the source command. - */ - - if (iPtr->deferredCallbacks) { - iPtr->deferredCallbacks = NULL; - } else { - TclNRAddCallback(interp, NRCommand, NULL, NULL, NULL, NULL); - } - - iPtr->numLevels++; - TclNRAddCallback(interp, EvalObjvCore, cmdPtr, INT2PTR(flags), - INT2PTR(objc), objv); - return TCL_OK; -} - -static int -EvalObjvCore( - void *data[], - Tcl_Interp *interp, - TCL_UNUSED(int) /*result*/) + * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE are + * currently supported. */ { - Command *cmdPtr = NULL, *preCmdPtr = (Command *)data[0]; - int flags = PTR2INT(data[1]); - Tcl_Size objc = PTR2INT(data[2]); - Tcl_Obj **objv = (Tcl_Obj **)data[3]; + Command *cmdPtr; Interp *iPtr = (Interp *) interp; - Namespace *lookupNsPtr = NULL; - int enterTracesDone = 0; - - /* - * Push records for task to be done on return, in INVERSE order. First, if - * needed, the exception handlers (as they should happen last). - */ - - if (!(flags & TCL_EVAL_NOERR)) { - TEOV_PushExceptionHandlers(interp, objc, objv, flags); - } + Tcl_Obj **newObjv; + int i; + CallFrame *savedVarFramePtr = NULL; + CallFrame *varFramePtr = iPtr->varFramePtr; + int code = TCL_OK; + int traceCode = TCL_OK; + int checkTraces = 1, traced; + Namespace *savedNsPtr = NULL; + Namespace *lookupNsPtr = iPtr->lookupNsPtr; + Tcl_Obj *commandPtr = NULL; - if (TCL_OK != TclInterpReady(interp)) { + if (TclInterpReady(interp) == TCL_ERROR) { return TCL_ERROR; } @@ -4777,150 +3594,113 @@ EvalObjvCore( return TCL_OK; } - if (TclLimitExceeded(iPtr->limit)) { - return TCL_ERROR; - } + /* + * If any execution traces rename or delete the current command, we may + * need (at most) two passes here. + */ + + reparseBecauseOfTraces: /* * Configure evaluation context to match the requested flags. */ - if (iPtr->lookupNsPtr) { + if (flags) { + if (flags & TCL_EVAL_INVOKE) { + savedNsPtr = varFramePtr->nsPtr; + if (lookupNsPtr) { + varFramePtr->nsPtr = lookupNsPtr; + iPtr->lookupNsPtr = NULL; + } else { + varFramePtr->nsPtr = iPtr->globalNsPtr; + } + } else if ((flags & TCL_EVAL_GLOBAL) + && (varFramePtr != iPtr->rootFramePtr) && !savedVarFramePtr) { + varFramePtr = iPtr->rootFramePtr; + savedVarFramePtr = iPtr->varFramePtr; + iPtr->varFramePtr = varFramePtr; + } + } - /* - * Capture the namespace we should do command name resolution in, as - * instructed by our caller sneaking it in to us in a private interp - * field. Clear that field right away so we cannot possibly have its - * use leak where it should not. The sneaky message pass is done. - * - * Use of this mechanism overrides the TCL_EVAL_GLOBAL flag. - * TODO: Is that a bug? - */ + /* + * 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. + */ - lookupNsPtr = iPtr->lookupNsPtr; - iPtr->lookupNsPtr = NULL; - } else if (flags & TCL_EVAL_INVOKE) { - lookupNsPtr = iPtr->globalNsPtr; - } else { + 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 */ - TclResetRewriteEnsemble(interp, 1); - - if (flags & TCL_EVAL_GLOBAL) { - TEOV_SwitchVarFrame(interp); - lookupNsPtr = iPtr->globalNsPtr; - } + iPtr->ensembleRewrite.sourceObjs = NULL; } /* - * Lookup the Command to dispatch. + * Call trace functions if needed. */ - reresolve: - assert(cmdPtr == NULL); - if (preCmdPtr) { + traced = (iPtr->tracePtr || (cmdPtr->flags & CMD_HAS_EXEC_TRACES)); + if (traced && checkTraces) { + int cmdEpoch = cmdPtr->cmdEpoch; + int newEpoch; + /* - * Caller gave it to us. - */ + * Insure that we have a correct nul-terminated command string for the + * trace code. + */ - if (!(preCmdPtr->flags & CMD_DEAD)) { - /* - * So long as it exists, use it. - */ + commandPtr = GetCommandSource(iPtr, command, length, objc, objv); + command = TclGetStringFromObj(commandPtr, &length); - cmdPtr = preCmdPtr; - } else if (flags & TCL_EVAL_NORESOLVE) { - /* - * When it's been deleted, and we're told not to attempt resolving - * it ourselves, all we can do is raise an error. - */ + /* + * 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. + */ - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "attempt to invoke a deleted command")); - Tcl_SetErrorCode(interp, "TCL", "EVAL", "DELETEDCOMMAND", (void *)NULL); - return TCL_ERROR; + cmdPtr->refCount++; + if (iPtr->tracePtr && (traceCode == TCL_OK)) { + traceCode = TclCheckInterpTraces(interp, command, length, + cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); } - } - if (cmdPtr == NULL) { - cmdPtr = TEOV_LookupCmdFromObj(interp, objv[0], lookupNsPtr); - if (!cmdPtr) { - return TEOV_NotFound(interp, objc, objv, lookupNsPtr); + if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) { + traceCode = TclCheckExecutionTraces(interp, command, length, + cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); } - } - - if (enterTracesDone || iPtr->tracePtr - || (cmdPtr->flags & CMD_HAS_EXEC_TRACES)) { - Tcl_Obj *commandPtr = TclGetSourceFromFrame( - flags & TCL_EVAL_SOURCE_IN_FRAME ? iPtr->cmdFramePtr : NULL, - objc, objv); - - Tcl_IncrRefCount(commandPtr); - if (!enterTracesDone) { - int code = TEOV_RunEnterTraces(interp, &cmdPtr, commandPtr, - objc, objv); - - /* - * Send any exception from enter traces back as an exception - * raised by the traced command. - * TODO: Is this a bug? Letting an execution trace BREAK or - * CONTINUE or RETURN in the place of the traced command? Would - * either converting all exceptions to TCL_ERROR, or just - * swallowing them be better? (Swallowing them has the problem of - * permanently hiding program errors.) - */ - - if (code != TCL_OK) { - Tcl_DecrRefCount(commandPtr); - return code; - } + newEpoch = cmdPtr->cmdEpoch; + TclCleanupCommandMacro(cmdPtr); - /* - * If the enter traces made the resolved cmdPtr unusable, go back - * and resolve again, but next time don't run enter traces again. - */ + /* + * 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 (cmdPtr == NULL) { - enterTracesDone = 1; + if (traceCode == TCL_OK && cmdEpoch != newEpoch) { + checkTraces = 0; + if (commandPtr) { Tcl_DecrRefCount(commandPtr); - goto reresolve; + commandPtr = NULL; } + goto reparseBecauseOfTraces; } - - /* - * Schedule leave traces. Raise the refCount on the resolved cmdPtr, - * so that when it passes to the leave traces we know it's still - * valid. - */ - - cmdPtr->refCount++; - TclNRAddCallback(interp, TEOV_RunLeaveTraces, INT2PTR(objc), - commandPtr, cmdPtr, objv); } - TclNRAddCallback(interp, Dispatch, - cmdPtr->nreProc ? cmdPtr->nreProc : cmdPtr->objProc, - cmdPtr->objClientData, INT2PTR(objc), objv); - return TCL_OK; -} - -static int -Dispatch( - void *data[], - Tcl_Interp *interp, - TCL_UNUSED(int) /*result*/) -{ - Tcl_ObjCmdProc *objProc = (Tcl_ObjCmdProc *)data[0]; - void *clientData = data[1]; - int objc = PTR2INT(data[2]); - Tcl_Obj **objv = (Tcl_Obj **)data[3]; - Interp *iPtr = (Interp *) interp; - #ifdef USE_DTRACE if (TCL_DTRACE_CMD_ARGS_ENABLED()) { - const char *a[10]; + char *a[10]; int i = 0; while (i < 10) { @@ -4931,315 +3711,172 @@ Dispatch( } if (TCL_DTRACE_CMD_INFO_ENABLED() && iPtr->cmdFramePtr) { Tcl_Obj *info = TclInfoFrame(interp, iPtr->cmdFramePtr); - const char *a[6]; Tcl_Size i[2]; + 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], a[4], a[5]); + TCL_DTRACE_CMD_INFO(a[0], a[1], a[2], a[3], i[0], i[1]); TclDecrRefCount(info); } - if ((TCL_DTRACE_CMD_RETURN_ENABLED() || TCL_DTRACE_CMD_RESULT_ENABLED()) - && objc) { - TclNRAddCallback(interp, DTraceCmdReturn, objv[0], NULL, NULL, NULL); - } - if (TCL_DTRACE_CMD_ENTRY_ENABLED() && objc) { - TCL_DTRACE_CMD_ENTRY(TclGetString(objv[0]), objc - 1, - (Tcl_Obj **)(objv + 1)); - } #endif /* USE_DTRACE */ - iPtr->cmdCount++; - return objProc(clientData, interp, objc, objv); -} - -int -TclNRRunCallbacks( - Tcl_Interp *interp, - int result, - struct NRE_callback *rootPtr) - /* All callbacks down to rootPtr not inclusive - * are to be run. */ -{ -#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 - Interp *iPtr = (Interp *) interp; -#endif /* !defined(TCL_NO_DEPRECATED) */ - - /* - * 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. - * - * This only needs to be done for the first item in the list: all other - * are for NR function calls, and those are Tcl_Obj based. - */ - -#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 - if (*(iPtr->result) != 0) { - (void) Tcl_GetObjResult(interp); - } -#endif /* !defined(TCL_NO_DEPRECATED) */ - /* - * This is the trampoline. + * Finally, invoke the command's Tcl_ObjCmdProc. */ - while (TOP_CB(interp) != rootPtr) { - NRE_callback *callbackPtr = TOP_CB(interp); - Tcl_NRPostProc *procPtr = callbackPtr->procPtr; - - TOP_CB(interp) = callbackPtr->nextPtr; - result = procPtr(callbackPtr->data, interp, result); - TCLNR_FREE(interp, callbackPtr); - } - return result; -} - -static int -NRCommand( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - Tcl_Obj *listPtr; - - iPtr->numLevels--; - - /* - * If there is a tailcall, schedule it next - */ - - if (data[1] && (data[1] != INT2PTR(1))) { - listPtr = (Tcl_Obj *)data[1]; - data[1] = NULL; - - TclNRAddCallback(interp, TclNRTailcallEval, listPtr, NULL, NULL, NULL); + cmdPtr->refCount++; + iPtr->cmdCount++; + 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)); + } + code = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv); + if (TCL_DTRACE_CMD_RETURN_ENABLED()) { + TCL_DTRACE_CMD_RETURN(TclGetString(objv[0]), code); + } } - /* OPT ?? - * Do not interrupt a series of cleanups with async or limit checks: - * just check at the end? - */ - if (TclAsyncReady(iPtr)) { - result = Tcl_AsyncInvoke(interp, result); - } - if ((result == TCL_OK) && TclCanceled(iPtr)) { - result = Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG); + code = Tcl_AsyncInvoke(interp, code); } - if (result == TCL_OK && TclLimitReady(iPtr->limit)) { - result = Tcl_LimitCheck(interp); + if (code == TCL_OK && TclLimitReady(iPtr->limit)) { + code = Tcl_LimitCheck(interp); } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * TEOV_Exception - - * TEOV_LookupCmdFromObj - - * TEOV_RunEnterTraces - - * TEOV_RunLeaveTraces - - * TEOV_NotFound - - * - * These are helper functions for Tcl_EvalObjv. - * - *---------------------------------------------------------------------- - */ - -static void -TEOV_PushExceptionHandlers( - Tcl_Interp *interp, - Tcl_Size objc, - Tcl_Obj *const objv[], - int flags) -{ - Interp *iPtr = (Interp *) interp; - /* - * If any error processing is necessary, push the appropriate records. - * Note that we have to push them in the inverse order: first the one that - * has to run last. + * Call 'leave' command traces */ - if (!(flags & TCL_EVAL_INVOKE)) { - /* - * Error messages - */ - - TclNRAddCallback(interp, TEOV_Error, INT2PTR(objc), - objv, NULL, NULL); - } + 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 (iPtr->numLevels == 1) { /* - * No CONTINUE or BREAK at level 0, manage RETURN + * 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. */ - TclNRAddCallback(interp, TEOV_Exception, INT2PTR(iPtr->evalFlags), - NULL, NULL, NULL); + if (traceCode != TCL_OK) { + code = traceCode; + } + if (commandPtr) { + Tcl_DecrRefCount(commandPtr); + } } -} - -static void -TEOV_SwitchVarFrame( - Tcl_Interp *interp) -{ - Interp *iPtr = (Interp *) interp; /* - * Change the varFrame to be the rootVarFrame, and push a record to - * restore things at the end. + * Decrement the reference count of cmdPtr and deallocate it if it has + * dropped to zero. */ - TclNRAddCallback(interp, TEOV_RestoreVarFrame, iPtr->varFramePtr, NULL, - NULL, NULL); - iPtr->varFramePtr = iPtr->rootFramePtr; -} - -static int -TEOV_RestoreVarFrame( - void *data[], - Tcl_Interp *interp, - int result) -{ - ((Interp *) interp)->varFramePtr = (CallFrame *)data[0]; - return result; -} - -static int -TEOV_Exception( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - int allowExceptions = (PTR2INT(data[0]) & TCL_ALLOW_EXCEPTIONS); - - if (result != TCL_OK) { - if (result == TCL_RETURN) { - result = TclUpdateReturnInfo(iPtr); - } - if ((result != TCL_OK) && (result != TCL_ERROR) && !allowExceptions) { - ProcessUnexpectedResult(interp, result); - result = TCL_ERROR; - } - } + TclCleanupCommandMacro(cmdPtr); /* - * We are returning to level 0, so should process TclResetCancellation. As - * numLevels has not *yet* been decreased, do not call it: do the thing - * here directly. + * 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. */ - TclUnsetCancelFlags(iPtr); - return result; -} - -static int -TEOV_Error( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - Tcl_Obj *listPtr; - const char *cmdString; - Tcl_Size cmdLen; - Tcl_Size objc = PTR2INT(data[0]); - Tcl_Obj **objv = (Tcl_Obj **)data[1]; + if (*(iPtr->result) != 0) { + (void) Tcl_GetObjResult(interp); + } - if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { - /* - * If there was an error, a command string will be needed for the - * error log: get it out of the itemPtr. The details depend on the - * type. - */ +#ifdef USE_DTRACE + if (TCL_DTRACE_CMD_RESULT_ENABLED()) { + Tcl_Obj *r; - listPtr = Tcl_NewListObj(objc, objv); - cmdString = TclGetStringFromObj(listPtr, &cmdLen); - Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen); - Tcl_DecrRefCount(listPtr); + r = Tcl_GetObjResult(interp); + TCL_DTRACE_CMD_RESULT(TclGetString(objv[0]), code, TclGetString(r),r); } - iPtr->flags &= ~ERR_ALREADY_LOGGED; - return result; -} +#endif /* USE_DTRACE */ -static int -TEOV_NotFound( - Tcl_Interp *interp, - Tcl_Size objc, - Tcl_Obj *const objv[], - Namespace *lookupNsPtr) -{ - Command * cmdPtr; - Interp *iPtr = (Interp *) interp; - Tcl_Size i, newObjc, handlerObjc; - Tcl_Obj **newObjv, **handlerObjv; - CallFrame *varFramePtr = iPtr->varFramePtr; - Namespace *currNsPtr = NULL;/* Used to check for and invoke any registered - * unknown command handler for the current - * namespace (TIP 181). */ - Namespace *savedNsPtr = NULL; + done: + if (savedVarFramePtr) { + iPtr->varFramePtr = savedVarFramePtr; + } + return code; - currNsPtr = varFramePtr->nsPtr; - if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) { - currNsPtr = iPtr->globalNsPtr; - if (currNsPtr == NULL) { - Tcl_Panic("TEOV_NotFound: NULL global namespace pointer"); + 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"); + } } - } - /* - * Check to see if the resolution namespace has lost its unknown handler. - * If so, reset it to "::unknown". - */ + /* + * Check to see if the resolution namespace has lost its unknown + * handler. If so, reset it to "::unknown". + */ - if (currNsPtr->unknownHandlerPtr == NULL) { - TclNewLiteralStringObj(currNsPtr->unknownHandlerPtr, "::unknown"); - Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr); - } + if (currNsPtr->unknownHandlerPtr == NULL) { + TclNewLiteralStringObj(currNsPtr->unknownHandlerPtr, "::unknown"); + Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr); + } - /* - * 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 invocation - * itself. - */ + /* + * 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. + */ - TclListObjGetElements(NULL, currNsPtr->unknownHandlerPtr, - &handlerObjc, &handlerObjv); - newObjc = objc + handlerObjc; - newObjv = (Tcl_Obj **)TclStackAlloc(interp, sizeof(Tcl_Obj *) * newObjc); + 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. - */ + /* + * 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]); - } - memcpy(newObjv+handlerObjc, objv, sizeof(Tcl_Obj *) * objc); + for (i = 0; i < handlerObjc; ++i) { + newObjv[i] = handlerObjv[i]; + Tcl_IncrRefCount(newObjv[i]); + } + memcpy(newObjv+handlerObjc, objv, sizeof(Tcl_Obj *) * (unsigned)objc); - /* - * 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. - * - * In this case we worry a bit less about recursion for now, and call the - * "blocking" interface. - */ + /* + * 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. + */ - cmdPtr = TEOV_LookupCmdFromObj(interp, newObjv[0], lookupNsPtr); - if (cmdPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "invalid command name \"%s\"", TclGetString(objv[0]))); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", - TclGetString(objv[0]), (void *)NULL); + 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--; + } /* * Release any resources we locked and allocated during the handler @@ -5250,165 +3887,89 @@ TEOV_NotFound( Tcl_DecrRefCount(newObjv[i]); } TclStackFree(interp, newObjv); - return TCL_ERROR; - } - - if (lookupNsPtr) { - savedNsPtr = varFramePtr->nsPtr; - varFramePtr->nsPtr = lookupNsPtr; - } - TclSkipTailcall(interp); - TclNRAddCallback(interp, TEOV_NotFoundCallback, INT2PTR(handlerObjc), - newObjv, savedNsPtr, NULL); - return TclNREvalObjv(interp, newObjc, newObjv, TCL_EVAL_NOERR, NULL); -} - -static int -TEOV_NotFoundCallback( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - Tcl_Size objc = PTR2INT(data[0]); - Tcl_Obj **objv = (Tcl_Obj **)data[1]; - Namespace *savedNsPtr = (Namespace *)data[2]; - - Tcl_Size i; - - if (savedNsPtr) { - iPtr->varFramePtr->nsPtr = savedNsPtr; - } - - /* - * Release any resources we locked and allocated during the handler call. - */ - - for (i = 0; i < objc; ++i) { - Tcl_DecrRefCount(objv[i]); + if (savedNsPtr) { + varFramePtr->nsPtr = savedNsPtr; + } + goto done; } - TclStackFree(interp, objv); - - return result; } + +/* + *---------------------------------------------------------------------- + * + * Tcl_EvalObjv -- + * + * This function 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. + * + *---------------------------------------------------------------------- + */ -static int -TEOV_RunEnterTraces( - Tcl_Interp *interp, - Command **cmdPtrPtr, - Tcl_Obj *commandPtr, - Tcl_Size objc, - Tcl_Obj *const objv[]) +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. */ { Interp *iPtr = (Interp *) interp; - Command *cmdPtr = *cmdPtrPtr; - Tcl_Size length, newEpoch, cmdEpoch = cmdPtr->cmdEpoch; - int traceCode = TCL_OK; - const char *command = TclGetStringFromObj(commandPtr, &length); - - /* - * Call trace functions. - * 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. - */ + int code = TCL_OK; + int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); - cmdPtr->refCount++; - if (iPtr->tracePtr) { - traceCode = TclCheckInterpTraces(interp, command, length, - cmdPtr, TCL_OK, TCL_TRACE_ENTER_EXEC, objc, objv); - } - if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) { - traceCode = TclCheckExecutionTraces(interp, command, length, - cmdPtr, TCL_OK, TCL_TRACE_ENTER_EXEC, objc, objv); - } - newEpoch = cmdPtr->cmdEpoch; - TclCleanupCommandMacro(cmdPtr); + iPtr->numLevels++; + code = TclEvalObjvInternal(interp, objc, objv, NULL, 0, flags); + iPtr->numLevels--; - if (traceCode != TCL_OK) { - if (traceCode == TCL_ERROR) { - Tcl_Obj *info; + if (code == TCL_OK) { + return code; + } else { - TclNewLiteralStringObj(info, "\n (enter trace on \""); - Tcl_AppendLimitedToObj(info, command, length, 55, "..."); - Tcl_AppendToObj(info, "\")", 2); - Tcl_AppendObjToErrorInfo(interp, info); - iPtr->flags |= ERR_ALREADY_LOGGED; - } - return traceCode; - } - if (cmdEpoch != newEpoch) { - *cmdPtrPtr = NULL; - } - return TCL_OK; -} + /* + * If we are again at the top level, process any unusual return code + * returned by the evaluated code. + */ -static int -TEOV_RunLeaveTraces( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - int traceCode = TCL_OK; - Tcl_Size objc = PTR2INT(data[0]); - Tcl_Obj *commandPtr = (Tcl_Obj *)data[1]; - Command *cmdPtr = (Command *)data[2]; - Tcl_Obj **objv = (Tcl_Obj **)data[3]; - Tcl_Size length; - const char *command = TclGetStringFromObj(commandPtr, &length); - - if (!(cmdPtr->flags & CMD_DYING)) { - if (cmdPtr->flags & CMD_HAS_EXEC_TRACES) { - traceCode = TclCheckExecutionTraces(interp, command, length, - cmdPtr, result, TCL_TRACE_LEAVE_EXEC, objc, objv); - } - if (iPtr->tracePtr != NULL && traceCode == TCL_OK) { - traceCode = TclCheckInterpTraces(interp, command, length, - cmdPtr, result, TCL_TRACE_LEAVE_EXEC, objc, objv); + if (iPtr->numLevels == 0) { + if (code == TCL_RETURN) { + code = TclUpdateReturnInfo(iPtr); + } + if ((code != TCL_ERROR) && !allowExceptions) { + ProcessUnexpectedResult(interp, code); + code = TCL_ERROR; + } } - } - /* - * As cmdPtr is set, TclNRRunCallbacks is about to reduce the numlevels. - * Prevent that by resetting the cmdPtr field and dealing right here with - * cmdPtr->refCount. - */ - - TclCleanupCommandMacro(cmdPtr); + 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. + */ - if (traceCode != TCL_OK) { - if (traceCode == TCL_ERROR) { - Tcl_Obj *info; + Tcl_Obj *listPtr; + char *cmdString; + int cmdLen; - TclNewLiteralStringObj(info, "\n (leave trace on \""); - Tcl_AppendLimitedToObj(info, command, length, 55, "..."); - Tcl_AppendToObj(info, "\")", 2); - Tcl_AppendObjToErrorInfo(interp, info); - iPtr->flags |= ERR_ALREADY_LOGGED; + listPtr = Tcl_NewListObj(objc, objv); + cmdString = Tcl_GetStringFromObj(listPtr, &cmdLen); + Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen); + Tcl_DecrRefCount(listPtr); } - result = traceCode; - } - Tcl_DecrRefCount(commandPtr); - return result; -} - -static inline Command * -TEOV_LookupCmdFromObj( - Tcl_Interp *interp, - Tcl_Obj *namePtr, - Namespace *lookupNsPtr) -{ - Interp *iPtr = (Interp *) interp; - Command *cmdPtr; - Namespace *savedNsPtr = iPtr->varFramePtr->nsPtr; - if (lookupNsPtr) { - iPtr->varFramePtr->nsPtr = lookupNsPtr; + return code; } - cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, namePtr); - iPtr->varFramePtr->nsPtr = savedNsPtr; - return cmdPtr; } /* @@ -5438,14 +3999,13 @@ Tcl_EvalTokensStandard( * errors. */ Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens to * evaluate and concatenate. */ - Tcl_Size count) /* Number of tokens to consider at tokenPtr. + int count) /* Number of tokens to consider at tokenPtr. * Must be at least 1. */ { return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL, 1, - NULL, NULL); + NULL, NULL); } -#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 /* *---------------------------------------------------------------------- * @@ -5480,7 +4040,7 @@ Tcl_EvalTokens( * errors. */ Tcl_Token *tokenPtr, /* Pointer to first in an array of tokens to * evaluate and concatenate. */ - Tcl_Size count) /* Number of tokens to consider at tokenPtr. + int count) /* Number of tokens to consider at tokenPtr. * Must be at least 1. */ { Tcl_Obj *resPtr; @@ -5493,7 +4053,6 @@ Tcl_EvalTokens( Tcl_ResetResult(interp); return resPtr; } -#endif /* !TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -5521,14 +4080,14 @@ 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. */ - Tcl_Size numBytes, /* Number of bytes in script. If -1, the + 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. */ { - return TclEvalEx(interp, script, numBytes, flags, 1, NULL, script); + return TclEvalEx(interp, script, numBytes, flags, 1, NULL, script); } int @@ -5536,64 +4095,66 @@ 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. */ - Tcl_Size numBytes, /* Number of bytes in script. If -1, the + int numBytes, /* Number of bytes in script. If < 0, the * script consists of all bytes up to the * first NUL character. */ int flags, /* Collection of OR-ed bits that control the * evaluation of the script. Only * TCL_EVAL_GLOBAL is currently supported. */ - Tcl_Size line, /* The line the script starts on. */ - Tcl_Size *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 referred to - * by 'script'. The 'clNextOuter' refers to - * the current entry in the table of - * continuation lines in this "main script", - * and the character offsets are relative to - * the 'outerScript' as well. - * - * If outerScript == script, then this call is - * for the outer-most script/command. See - * Tcl_EvalEx() and TclEvalObjEx() for places - * generating arguments for which this is - * true. */ + 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 + * '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 the outer-most script/command. See + * Tcl_EvalEx() and TclEvalObjEx() for places + * generating arguments for which this is true. + */ { Interp *iPtr = (Interp *) interp; const char *p, *next; - const int minObjs = 20; + const unsigned int minObjs = 20; Tcl_Obj **objv, **objvSpace; - int *expand; - Tcl_Size *lines, *lineSpace; + int *expand, *lines, *lineSpace; Tcl_Token *tokenPtr; - int expandRequested, code = TCL_OK; - Tcl_Size bytesLeft, commandLength; + 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); int gotParse = 0; - Tcl_Size i, objectsUsed = 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_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)); - Tcl_Size *linesStack = (Tcl_Size *)TclStackAlloc(interp, minObjs * sizeof(Tcl_Size)); + int *expandStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int)); + int *linesStack = (int *) TclStackAlloc(interp, minObjs * sizeof(int)); /* TIP #280 Structures for tracking of command * locations. */ - Tcl_Size *clNext = NULL; /* Pointer for the tracking of invisible - * continuation lines. Initialized only if the - * caller gave us a table of locations to - * track, via scriptCLLocPtr. It always refers - * to the table entry holding the location of - * the next invisible continuation line to - * look for, while parsing the script. */ + /* + * Pointer for the tracking of invisible continuation lines. Initialized + * only if the caller gave us a table of locations to track, via + * scriptCLLocPtr. It always refers to the table entry holding the + * location of the next invisible continuation line to look for, while + * parsing the script. + */ + + int* clNext = NULL; if (iPtr->scriptCLLocPtr) { if (clNextOuter) { @@ -5627,22 +4188,23 @@ TclEvalEx( /* * TIP #280 Initialize tracking. Do not push on the frame stack yet. * - * We 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. + * 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. */ - eeFramePtr->level = iPtr->cmdFramePtr ? iPtr->cmdFramePtr->level + 1 : 1; - eeFramePtr->framePtr = iPtr->framePtr; - eeFramePtr->nextPtr = iPtr->cmdFramePtr; - eeFramePtr->nline = 0; - eeFramePtr->line = NULL; - eeFramePtr->cmdObj = NULL; + if (iPtr->evalFlags & TCL_EVAL_CTX) { + /* + * Path information comes out of the context. + */ - iPtr->cmdFramePtr = eeFramePtr; - if (iPtr->evalFlags & TCL_EVAL_FILE) { + 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. */ @@ -5663,7 +4225,6 @@ TclEvalEx( /* * Error message in the interp result. */ - code = TCL_ERROR; goto error; } @@ -5681,13 +4242,17 @@ TclEvalEx( eeFramePtr->data.eval.path = NULL; } + 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, 0, parsePtr) != TCL_OK) { code = TCL_ERROR; - Tcl_LogCommandInfo(interp, script, parsePtr->commandStart, - parsePtr->term + 1 - parsePtr->commandStart); - goto posterror; + goto error; } /* @@ -5697,8 +4262,8 @@ TclEvalEx( */ TclAdvanceLines(&line, p, parsePtr->commandStart); - TclAdvanceContinuations(&line, &clNext, - parsePtr->commandStart - outerScript); + TclAdvanceContinuations (&line, &clNext, + parsePtr->commandStart - outerScript); gotParse = 1; if (parsePtr->numWords > 0) { @@ -5709,26 +4274,27 @@ TclEvalEx( * per-command parsing. */ - Tcl_Size wordLine = line; + int wordLine = line; const char *wordStart = parsePtr->commandStart; - Tcl_Size *wordCLNext = clNext; - Tcl_Size objectsNeeded = 0; - Tcl_Size numWords = parsePtr->numWords; + int* wordCLNext = clNext; /* * Generate an array of objects for the words of the command. */ + unsigned int objectsNeeded = 0; + unsigned int numWords = parsePtr->numWords; + if (numWords > minObjs) { - expand = (int *)ckalloc(numWords * sizeof(int)); - objvSpace = (Tcl_Obj **)ckalloc(numWords * sizeof(Tcl_Obj *)); - lineSpace = (Tcl_Size *)ckalloc(numWords * sizeof(Tcl_Size)); + 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; - iPtr->cmdFramePtr = eeFramePtr->nextPtr; for (objectsUsed = 0, tokenPtr = parsePtr->tokenPtr; objectsUsed < numWords; objectsUsed++, tokenPtr += tokenPtr->numComponents+1) { @@ -5741,8 +4307,8 @@ TclEvalEx( */ TclAdvanceLines(&wordLine, wordStart, tokenPtr->start); - TclAdvanceContinuations(&wordLine, &wordCLNext, - tokenPtr->start - outerScript); + TclAdvanceContinuations (&wordLine, &wordCLNext, + tokenPtr->start - outerScript); wordStart = tokenPtr->start; lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL) @@ -5754,17 +4320,17 @@ TclEvalEx( code = TclSubstTokens(interp, tokenPtr+1, tokenPtr->numComponents, NULL, wordLine, - wordCLNext, outerScript); + wordCLNext, outerScript); iPtr->evalFlags = 0; if (code != TCL_OK) { - break; + goto error; } objv[objectsUsed] = Tcl_GetObjResult(interp); Tcl_IncrRefCount(objv[objectsUsed]); if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) { - Tcl_Size numElements; + int numElements; code = TclListObjLength(interp, objv[objectsUsed], &numElements); @@ -5774,9 +4340,9 @@ TclEvalEx( */ Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (expanding word %" TCL_SIZE_MODIFIER "d)", objectsUsed)); + "\n (expanding word %d)", objectsUsed)); Tcl_DecrRefCount(objv[objectsUsed]); - break; + goto error; } expandRequested = 1; expand[objectsUsed] = 1; @@ -5788,37 +4354,34 @@ TclEvalEx( } if (wordCLNext) { - TclContinuationsEnterDerived(objv[objectsUsed], - wordStart - outerScript, wordCLNext); + TclContinuationsEnterDerived (objv[objectsUsed], + wordStart - outerScript, wordCLNext); } } /* for loop */ - iPtr->cmdFramePtr = eeFramePtr; - if (code != TCL_OK) { - goto error; - } if (expandRequested) { /* * Some word expansion was requested. Check for objv resize. */ Tcl_Obj **copy = objvSpace; - Tcl_Size *lcopy = lineSpace; - Tcl_Size wordIdx = numWords; - Tcl_Size objIdx = objectsNeeded - 1; - - if ((numWords > minObjs) || (objectsNeeded > minObjs)) { - objv = objvSpace = - (Tcl_Obj **)ckalloc(objectsNeeded * sizeof(Tcl_Obj *)); - lines = lineSpace = (Tcl_Size *)ckalloc(objectsNeeded * sizeof(Tcl_Size)); + 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]) { - Tcl_Size numElements; + int numElements; Tcl_Obj **elements, *temp = copy[wordIdx]; - TclListObjGetElements(NULL, temp, &numElements, + Tcl_ListObjGetElements(NULL, temp, &numElements, &elements); objectsUsed += numElements; while (numElements--) { @@ -5836,10 +4399,10 @@ TclEvalEx( objv += objIdx+1; if (copy != stackObjArray) { - ckfree(copy); + ckfree((char *) copy); } if (lcopy != linesStack) { - ckfree(lcopy); + ckfree((char *) lcopy); } } @@ -5853,28 +4416,28 @@ TclEvalEx( * have been executed. */ - eeFramePtr->cmd = parsePtr->commandStart; - eeFramePtr->len = parsePtr->commandSize; + eeFramePtr->cmd.str.cmd = parsePtr->commandStart; + eeFramePtr->cmd.str.len = parsePtr->commandSize; if (parsePtr->term == parsePtr->commandStart + parsePtr->commandSize - 1) { - eeFramePtr->len--; + eeFramePtr->cmd.str.len--; } eeFramePtr->nline = objectsUsed; eeFramePtr->line = lines; - TclArgumentEnter(interp, objv, objectsUsed, eeFramePtr); - code = Tcl_EvalObjv(interp, objectsUsed, objv, - TCL_EVAL_NOERR | TCL_EVAL_SOURCE_IN_FRAME); - TclArgumentRelease(interp, objv, objectsUsed); + TclArgumentEnter (interp, objv, objectsUsed, eeFramePtr); + iPtr->cmdFramePtr = eeFramePtr; + iPtr->numLevels++; + code = TclEvalObjvInternal(interp, objectsUsed, objv, + parsePtr->commandStart, parsePtr->commandSize, 0); + iPtr->numLevels--; + iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; + TclArgumentRelease (interp, objv, objectsUsed); eeFramePtr->line = NULL; eeFramePtr->nline = 0; - if (eeFramePtr->cmdObj) { - Tcl_DecrRefCount(eeFramePtr->cmdObj); - eeFramePtr->cmdObj = NULL; - } if (code != TCL_OK) { goto error; @@ -5884,9 +4447,9 @@ TclEvalEx( } objectsUsed = 0; if (objvSpace != stackObjArray) { - ckfree(objvSpace); + ckfree((char *) objvSpace); objvSpace = stackObjArray; - ckfree(lineSpace); + ckfree((char *) lineSpace); lineSpace = linesStack; } @@ -5896,7 +4459,7 @@ TclEvalEx( */ if (expand != expandStack) { - ckfree(expand); + ckfree((char *) expand); expand = expandStack; } } @@ -5948,7 +4511,6 @@ TclEvalEx( Tcl_LogCommandInfo(interp, script, parsePtr->commandStart, commandLength); } - posterror: iPtr->flags &= ~ERR_ALREADY_LOGGED; /* @@ -5962,11 +4524,11 @@ TclEvalEx( Tcl_FreeParse(parsePtr); } if (objvSpace != stackObjArray) { - ckfree(objvSpace); - ckfree(lineSpace); + ckfree((char *) objvSpace); + ckfree((char *) lineSpace); } if (expand != expandStack) { - ckfree(expand); + ckfree((char *) expand); } iPtr->varFramePtr = savedVarFramePtr; @@ -5975,7 +4537,6 @@ TclEvalEx( * TIP #280. Release the local CmdFrame, and its contents. */ - iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; if (eeFramePtr->type == TCL_LOCATION_SOURCE) { Tcl_DecrRefCount(eeFramePtr->data.eval.path); } @@ -6008,11 +4569,11 @@ TclEvalEx( void TclAdvanceLines( - Tcl_Size *line, + int *line, const char *start, const char *end) { - const char *p; + register const char *p; for (p = start; p < end; p++) { if (*p == '\n') { @@ -6042,31 +4603,29 @@ TclAdvanceLines( */ void -TclAdvanceContinuations( - Tcl_Size *line, - Tcl_Size **clNextPtrPtr, - int loc) +TclAdvanceContinuations (line,clNextPtrPtr,loc) + int* line; + int** clNextPtrPtr; + int loc; { /* - * Track the invisible continuation lines embedded in a script, if any. - * Here they are just spaces (already). They were removed by - * TclSubstTokens via TclParseBackslash. + * Track the invisible continuation lines embedded in a script, if + * any. Here they are just spaces (already). They were removed by + * 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. */ - while (*clNextPtrPtr && (**clNextPtrPtr >= 0) - && (loc >= **clNextPtrPtr)) { + while (*clNextPtrPtr && (**clNextPtrPtr >= 0) && (loc >= **clNextPtrPtr)) { /* * We just stepped over an invisible continuation line. Adjust the * line counter and step to the table entry holding the location of * the next continuation line to track. */ - - (*line)++; - (*clNextPtrPtr)++; + (*line) ++; + (*clNextPtrPtr) ++; } } @@ -6084,8 +4643,8 @@ TclAdvanceContinuations( * * TclArgumentEnter -- * - * This procedure is a helper for the TIP #280 uplevel extension. It - * enters location references for the arguments of a command to be + * This procedure is a helper for the TIP #280 uplevel extension. + * It enters location references for the arguments of a command to be * invoked. Only the first entry has the actual data, further entries * simply count the usage up. * @@ -6100,50 +4659,45 @@ TclAdvanceContinuations( */ void -TclArgumentEnter( - Tcl_Interp *interp, - Tcl_Obj **objv, - Tcl_Size objc, - CmdFrame *cfPtr) -{ - Interp *iPtr = (Interp *) interp; - int isNew; - Tcl_Size i; - Tcl_HashEntry *hPtr; - CFWord *cfwPtr; - - for (i = 1; i < objc; i++) { +TclArgumentEnter(interp,objv,objc,cfPtr) + Tcl_Interp* interp; + Tcl_Obj** objv; + int objc; + CmdFrame* cfPtr; +{ + Interp* iPtr = (Interp*) interp; + int new, i; + Tcl_HashEntry* hPtr; + CFWord* cfwPtr; + + for (i=1; i < objc; i++) { /* - * Ignore argument words without line information (= dynamic). If they - * are variables they may have location information associated with - * that, either through globally recorded 'set' invocations, or - * literals in bytecode. Either way there is no need to record + * Ignore argument words without line information (= dynamic). If + * they are variables they may have location information associated + * with that, either through globally recorded 'set' invokations, or + * literals in bytecode. Eitehr way there is no need to record * something here. */ - if (cfPtr->line[i] < 0) { - continue; - } - hPtr = Tcl_CreateHashEntry(iPtr->lineLAPtr, objv[i], &isNew); - if (isNew) { - /* - * The word is not on the stack yet, remember the current location - * and initialize references. - */ - - cfwPtr = (CFWord *)ckalloc(sizeof(CFWord)); - cfwPtr->framePtr = cfPtr; - cfwPtr->word = i; - cfwPtr->refCount = 1; - Tcl_SetHashValue(hPtr, cfwPtr); + if (cfPtr->line [i] < 0) continue; + hPtr = Tcl_CreateHashEntry (iPtr->lineLAPtr, (char*) objv[i], &new); + if (new) { + /* + * The word is not on the stack yet, remember the current location + * and initialize references. + */ + cfwPtr = (CFWord*) ckalloc (sizeof (CFWord)); + cfwPtr->framePtr = cfPtr; + cfwPtr->word = i; + cfwPtr->refCount = 1; + Tcl_SetHashValue (hPtr, cfwPtr); } else { - /* - * The word is already on the stack, its current location is not - * relevant. Just remember the reference to prevent early removal. - */ - - cfwPtr = (CFWord *)Tcl_GetHashValue(hPtr); - cfwPtr->refCount++; + /* + * The word is already on the stack, its current location is not + * relevant. Just remember the reference to prevent early removal. + */ + cfwPtr = (CFWord*) Tcl_GetHashValue (hPtr); + cfwPtr->refCount ++; } } } @@ -6153,10 +4707,10 @@ TclArgumentEnter( * * TclArgumentRelease -- * - * This procedure is a helper for the TIP #280 uplevel extension. It - * removes the location references for the arguments of a command just - * done. Usage is counted down, the data is removed only when no user is - * left over. + * This procedure is a helper for the TIP #280 uplevel extension. + * It removes the location references for the arguments of a command + * just done. Usage is counted down, the data is removed only when + * no user is left over. * * Results: * None. @@ -6169,30 +4723,27 @@ TclArgumentEnter( */ void -TclArgumentRelease( - Tcl_Interp *interp, - Tcl_Obj **objv, - Tcl_Size objc) -{ - Interp *iPtr = (Interp *) interp; - Tcl_Size i; +TclArgumentRelease(interp,objv,objc) + Tcl_Interp* interp; + Tcl_Obj** objv; + int objc; +{ + Interp* iPtr = (Interp*) interp; + Tcl_HashEntry* hPtr; + CFWord* cfwPtr; + int i; - for (i = 1; i < objc; i++) { - CFWord *cfwPtr; - Tcl_HashEntry *hPtr = - Tcl_FindHashEntry(iPtr->lineLAPtr, objv[i]); + for (i=1; i < objc; i++) { + hPtr = Tcl_FindHashEntry (iPtr->lineLAPtr, (char *) objv[i]); - if (!hPtr) { - continue; - } - cfwPtr = (CFWord *)Tcl_GetHashValue(hPtr); + if (!hPtr) { continue; } + cfwPtr = (CFWord*) Tcl_GetHashValue (hPtr); - if (cfwPtr->refCount-- > 1) { - continue; - } + cfwPtr->refCount --; + if (cfwPtr->refCount > 0) { continue; } - ckfree(cfwPtr); - Tcl_DeleteHashEntry(hPtr); + ckfree ((char*) cfwPtr); + Tcl_DeleteHashEntry (hPtr); } } @@ -6201,9 +4752,9 @@ TclArgumentRelease( * * TclArgumentBCEnter -- * - * 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 invoked. Only the first entry has the actual + * 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 invoked. Only the first entry has the actual * data, further entries simply count the usage up. * * Results: @@ -6217,94 +4768,73 @@ TclArgumentRelease( */ void -TclArgumentBCEnter( - Tcl_Interp *interp, - Tcl_Obj *objv[], - Tcl_Size objc, - void *codePtr, - CmdFrame *cfPtr, - Tcl_Size cmd, - Tcl_Size pc) -{ - ExtCmdLoc *eclPtr; - Tcl_Size word; - ECL *ePtr; - CFWordBC *lastPtr = NULL; - Interp *iPtr = (Interp *) interp; - Tcl_HashEntry *hePtr = - Tcl_FindHashEntry(iPtr->lineBCPtr, codePtr); - - if (!hePtr) { - return; - } - eclPtr = (ExtCmdLoc *)Tcl_GetHashValue(hePtr); - ePtr = &eclPtr->loc[cmd]; +TclArgumentBCEnter(interp,objv,objc,codePtr,cfPtr,pc) + Tcl_Interp* interp; + Tcl_Obj* objv[]; + int objc; + void* codePtr; + CmdFrame* cfPtr; + int pc; +{ + Interp* iPtr = (Interp*) interp; + Tcl_HashEntry* hePtr = Tcl_FindHashEntry (iPtr->lineBCPtr, (char *) codePtr); + + if (hePtr) { + ExtCmdLoc* eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hePtr); + hePtr = Tcl_FindHashEntry(&eclPtr->litInfo, INT2PTR(pc)); + + if (hePtr) { + int cmd = PTR2INT(Tcl_GetHashValue(hePtr)); + ECL* ePtr = &eclPtr->loc[cmd]; + int word; - /* - * ePtr->nline is the number of words originally parsed. - * - * objc is the number of elements getting invoked. - * - * If they are not the same, we arrived here by compiling an - * ensemble dispatch. Ensemble subcommands that lead to script - * evaluation are not supposed to get compiled, because a command - * such as [info level] in the script can expose some of the dispatch - * shenanigans. This means that we don't have to tend to the - * housekeeping, and can escape now. - */ - - if (ePtr->nline != objc) { - return; - } - - /* - * Having disposed of the ensemble cases, we can state... - * A few truths ... - * (1) ePtr->nline == objc - * (2) (ePtr->line[word] < 0) => !literal, for all words - * (3) (word == 0) => !literal - * - * Item (2) is why we can use objv to get the literals, and do not - * have to save them at compile time. - */ - - for (word = 1; word < objc; word++) { - if (ePtr->line[word] >= 0) { - int isNew; - Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(iPtr->lineLABCPtr, - objv[word], &isNew); - CFWordBC *cfwPtr = (CFWordBC *)ckalloc(sizeof(CFWordBC)); - - cfwPtr->framePtr = cfPtr; - cfwPtr->obj = objv[word]; - cfwPtr->pc = pc; - cfwPtr->word = word; - cfwPtr->nextPtr = lastPtr; - lastPtr = cfwPtr; + /* + * A few truths ... + * (1) ePtr->nline == objc + * (2) (ePtr->line[word] < 0) => !literal, for all words + * (3) (word == 0) => !literal + * + * Item (2) is why we can use objv to get the literals, and do not + * have to save them at compile time. + */ - if (isNew) { - /* - * The word is not on the stack yet, remember the current - * location and initialize references. - */ + if (ePtr->nline != objc) { + Tcl_Panic ("TIP 280 data structure inconsistency"); + } - cfwPtr->prevPtr = NULL; - } else { - /* - * The object is already on the stack, however it may have - * a different location now (literal sharing may map - * multiple location to a single Tcl_Obj*. Save the old - * information in the new structure. - */ + for (word = 1; word < objc; word++) { + if (ePtr->line[word] >= 0) { + int isnew; + Tcl_HashEntry* hPtr = + Tcl_CreateHashEntry (iPtr->lineLABCPtr, + (char*) objv[word], &isnew); + CFWordBC* cfwPtr = (CFWordBC*) ckalloc (sizeof (CFWordBC)); - cfwPtr->prevPtr = (CFWordBC *)Tcl_GetHashValue(hPtr); - } + cfwPtr->framePtr = cfPtr; + cfwPtr->pc = pc; + cfwPtr->word = word; - Tcl_SetHashValue(hPtr, cfwPtr); - } - } /* for */ + if (isnew) { + /* + * The word is not on the stack yet, remember the + * current location and initialize references. + */ + cfwPtr->prevPtr = NULL; + } else { + /* + * The object is already on the stack, however it may + * have a different location now (literal sharing may + * map multiple location to a single Tcl_Obj*. Save + * the old information in the new structure. + */ + cfwPtr->prevPtr = (CFWordBC*) Tcl_GetHashValue(hPtr); + } - cfPtr->litarg = lastPtr; + Tcl_SetHashValue (hPtr, cfwPtr); + } + } /* for */ + } /* if */ + } /* if */ } /* @@ -6312,10 +4842,10 @@ TclArgumentBCEnter( * * TclArgumentBCRelease -- * - * This procedure is a helper for the TIP #280 uplevel extension. It - * removes the location references for the literal arguments of commands - * in bytecode just done. Usage is counted down, the data is removed only - * when no user is left over. + * This procedure is a helper for the TIP #280 uplevel extension. + * It removes the location references for the literal arguments of + * commands in bytecode just done. Usage is counted down, the data + * is removed only when no user is left over. * * Results: * None. @@ -6328,34 +4858,48 @@ TclArgumentBCEnter( */ void -TclArgumentBCRelease( - Tcl_Interp *interp, - CmdFrame *cfPtr) +TclArgumentBCRelease(interp,objv,objc,codePtr,pc) + Tcl_Interp* interp; + Tcl_Obj* objv[]; + int objc; + void* codePtr; + int pc; { - Interp *iPtr = (Interp *) interp; - CFWordBC *cfwPtr = (CFWordBC *) cfPtr->litarg; + Interp* iPtr = (Interp*) interp; + Tcl_HashEntry* hePtr = Tcl_FindHashEntry (iPtr->lineBCPtr, (char *) codePtr); - while (cfwPtr) { - CFWordBC *nextPtr = cfwPtr->nextPtr; - Tcl_HashEntry *hPtr = - Tcl_FindHashEntry(iPtr->lineLABCPtr, cfwPtr->obj); - CFWordBC *xPtr = (CFWordBC *)Tcl_GetHashValue(hPtr); + if (hePtr) { + ExtCmdLoc* eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hePtr); + hePtr = Tcl_FindHashEntry(&eclPtr->litInfo, INT2PTR(pc)); - if (xPtr != cfwPtr) { - Tcl_Panic("TclArgumentBC Enter/Release Mismatch"); - } + if (hePtr) { + int cmd = PTR2INT(Tcl_GetHashValue(hePtr)); + ECL* ePtr = &eclPtr->loc[cmd]; + int word; - if (cfwPtr->prevPtr) { - Tcl_SetHashValue(hPtr, cfwPtr->prevPtr); - } else { - Tcl_DeleteHashEntry(hPtr); - } + /* + * Iterate in reverse order, to properly match our pop to the push + * in TclArgumentBCEnter(). + */ + for (word = objc-1; word >= 1; word--) { + if (ePtr->line[word] >= 0) { + Tcl_HashEntry* hPtr = Tcl_FindHashEntry(iPtr->lineLABCPtr, + (char *) objv[word]); + if (hPtr) { + CFWordBC* cfwPtr = (CFWordBC*) Tcl_GetHashValue (hPtr); + + if (cfwPtr->prevPtr) { + Tcl_SetHashValue(hPtr, cfwPtr->prevPtr); + } else { + Tcl_DeleteHashEntry(hPtr); + } - ckfree(cfwPtr); - cfwPtr = nextPtr; + ckfree((char *) cfwPtr); + } + } + } + } } - - cfPtr->litarg = NULL; } /* @@ -6363,8 +4907,8 @@ TclArgumentBCRelease( * * TclArgumentGet -- * - * This procedure is a helper for the TIP #280 uplevel extension. It - * finds the location references for a Tcl_Obj, if any. + * This procedure is a helper for the TIP #280 uplevel extension. + * It find the location references for a Tcl_Obj, if any. * * Results: * None. @@ -6377,15 +4921,15 @@ TclArgumentBCRelease( */ void -TclArgumentGet( - Tcl_Interp *interp, - Tcl_Obj *obj, - CmdFrame **cfPtrPtr, - int *wordPtr) +TclArgumentGet(interp,obj,cfPtrPtr,wordPtr) + Tcl_Interp* interp; + Tcl_Obj* obj; + CmdFrame** cfPtrPtr; + int* wordPtr; { - Interp *iPtr = (Interp *) interp; - Tcl_HashEntry *hPtr; - CmdFrame *framePtr; + Interp* iPtr = (Interp*) interp; + Tcl_HashEntry* hPtr; + CmdFrame* framePtr; /* * An object which either has no string rep or else is a canonical list is @@ -6394,7 +4938,7 @@ TclArgumentGet( * up by the caller. It knows better than us. */ - if (!TclHasStringRep(obj) || TclListObjIsCanonical(obj)) { + if ((obj->bytes == NULL) || TclListObjIsCanonical(obj)) { return; } @@ -6403,11 +4947,10 @@ TclArgumentGet( * stack. That is nearest. */ - hPtr = Tcl_FindHashEntry(iPtr->lineLAPtr, obj); + hPtr = Tcl_FindHashEntry (iPtr->lineLAPtr, (char *) obj); if (hPtr) { - CFWord *cfwPtr = (CFWord *)Tcl_GetHashValue(hPtr); - - *wordPtr = cfwPtr->word; + CFWord* cfwPtr = (CFWord*) Tcl_GetHashValue (hPtr); + *wordPtr = cfwPtr->word; *cfPtrPtr = cfwPtr->framePtr; return; } @@ -6417,15 +4960,16 @@ TclArgumentGet( * that stack. */ - hPtr = Tcl_FindHashEntry(iPtr->lineLABCPtr, obj); + hPtr = Tcl_FindHashEntry (iPtr->lineLABCPtr, (char *) obj); + if (hPtr) { - CFWordBC *cfwPtr = (CFWordBC *)Tcl_GetHashValue(hPtr); + CFWordBC* cfwPtr = (CFWordBC*) Tcl_GetHashValue (hPtr); framePtr = cfwPtr->framePtr; - framePtr->data.tebc.pc = (char *) (((ByteCode *) + framePtr->data.tebc.pc = (char *) (((ByteCode*) framePtr->data.tebc.codePtr)->codeStart + cfwPtr->pc); *cfPtrPtr = cfwPtr->framePtr; - *wordPtr = cfwPtr->word; + *wordPtr = cfwPtr->word; return; } } @@ -6452,15 +4996,13 @@ TclArgumentGet( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED -#undef Tcl_Eval int 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, script, TCL_INDEX_NONE, 0); + int code = Tcl_EvalEx(interp, script, -1, 0); /* * For backwards compatibility with old C code that predates the object @@ -6497,6 +5039,7 @@ Tcl_EvalObj( { return Tcl_EvalObjEx(interp, objPtr, 0); } + #undef Tcl_GlobalEvalObj int Tcl_GlobalEvalObj( @@ -6505,7 +5048,6 @@ Tcl_GlobalEvalObj( { return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL); } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -6516,11 +5058,6 @@ Tcl_GlobalEvalObj( * compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT is * specified. * - * If the flag TCL_EVAL_DIRECT is passed in, the value of invoker - * must be NULL. Support for non-NULL invokers in that mode has - * been removed since it was unused and untested. Failure to - * follow this limitation will lead to an assertion panic. - * * 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 @@ -6539,7 +5076,7 @@ int Tcl_EvalObjEx( Tcl_Interp *interp, /* Token for command interpreter (returned by * a previous call to Tcl_CreateInterp). */ - Tcl_Obj *objPtr, /* Pointer to object containing commands to + 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 @@ -6552,26 +5089,7 @@ int TclEvalObjEx( Tcl_Interp *interp, /* Token for command interpreter (returned by * a previous call to Tcl_CreateInterp). */ - 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. */ -{ - int result = TCL_OK; - NRE_callback *rootPtr = TOP_CB(interp); - - result = TclNREvalObjEx(interp, objPtr, flags, invoker, word); - return TclNRRunCallbacks(interp, result, rootPtr); -} - -int -TclNREvalObjEx( - Tcl_Interp *interp, /* Token for command interpreter (returned by - * a previous call to Tcl_CreateInterp). */ - Tcl_Obj *objPtr, /* Pointer to object containing commands to + 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 @@ -6579,128 +5097,88 @@ TclNREvalObjEx( const CmdFrame *invoker, /* Frame of the command doing the eval. */ int word) /* Index of the word which is in objPtr. */ { - Interp *iPtr = (Interp *) interp; + 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. */ - /* - * This function consists of three independent blocks for: direct - * evaluation of canonical lists, compilation and bytecode execution and - * finally direct evaluation. Precisely one of these blocks will be run. + Tcl_IncrRefCount(objPtr); + + /* 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)) { - CmdFrame *eoFramePtr = NULL; - Tcl_Size objc; - Tcl_Obj *listPtr, **objv; - /* - * Canonical List Optimization: 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. + * TIP #280 Structures for tracking lines. As we know that this is + * dynamic execution we ignore the invoker, even if known. */ - /* - * Shimmer protection! Always pass an unshared obj. The caller could - * incr the refCount of objPtr AFTER calling us! To be completely safe - * we always make a copy. The callback takes care of the refCounts for - * both listPtr and objPtr. - * - * TODO: Create a test to demo this need, or eliminate it. - * FIXME OPT: preserve just the internal rep? - */ - - Tcl_IncrRefCount(objPtr); - listPtr = TclListObjCopy(interp, objPtr); - Tcl_IncrRefCount(listPtr); - - if (word != INT_MIN) { - /* - * TIP #280 Structures for tracking lines. As we know that this is - * dynamic execution we ignore the invoker, even if known. - * - * 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. - * - * Note that we use (word==INTMIN) to signal that no command frame - * should be pushed, as needed by alias and ensemble redirections. - */ - - eoFramePtr = (CmdFrame *)TclStackAlloc(interp, sizeof(CmdFrame)); - eoFramePtr->nline = 0; - eoFramePtr->line = NULL; - - eoFramePtr->type = TCL_LOCATION_EVAL; - eoFramePtr->level = (iPtr->cmdFramePtr == NULL? - 1 : iPtr->cmdFramePtr->level + 1); - eoFramePtr->framePtr = iPtr->framePtr; - eoFramePtr->nextPtr = iPtr->cmdFramePtr; + int nelements; + Tcl_Obj **elements, *copyPtr = TclListObjCopy(NULL, objPtr); + CmdFrame *eoFramePtr = (CmdFrame *) + TclStackAlloc(interp, sizeof(CmdFrame)); - eoFramePtr->cmdObj = objPtr; - eoFramePtr->cmd = NULL; - eoFramePtr->len = 0; - eoFramePtr->data.eval.path = NULL; + eoFramePtr->type = TCL_LOCATION_EVAL_LIST; + eoFramePtr->level = (iPtr->cmdFramePtr == NULL? 1 + : iPtr->cmdFramePtr->level + 1); + eoFramePtr->framePtr = iPtr->framePtr; + eoFramePtr->nextPtr = iPtr->cmdFramePtr; - iPtr->cmdFramePtr = eoFramePtr; + eoFramePtr->nline = 0; + eoFramePtr->line = NULL; - flags |= TCL_EVAL_SOURCE_IN_FRAME; - } + eoFramePtr->cmd.listPtr = objPtr; + Tcl_IncrRefCount(eoFramePtr->cmd.listPtr); + eoFramePtr->data.eval.path = NULL; - TclMarkTailcall(interp); - TclNRAddCallback(interp, TEOEx_ListCallback, listPtr, eoFramePtr, - objPtr, NULL); - - TclListObjGetElements(NULL, listPtr, &objc, &objv); - return TclNREvalObjv(interp, objc, objv, flags, NULL); - } - - if (!(flags & TCL_EVAL_DIRECT)) { /* - * 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 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. */ - int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); - ByteCode *codePtr; - CallFrame *savedVarFramePtr = NULL; /* Saves old copy of - * iPtr->varFramePtr in case - * TCL_EVAL_GLOBAL was set. */ - - if (TclInterpReady(interp) != TCL_OK) { - return TCL_ERROR; - } - if (flags & TCL_EVAL_GLOBAL) { - savedVarFramePtr = iPtr->varFramePtr; - iPtr->varFramePtr = iPtr->rootFramePtr; - } - Tcl_IncrRefCount(objPtr); - codePtr = TclCompileObj(interp, objPtr, invoker, word); + Tcl_ListObjGetElements(NULL, copyPtr, &nelements, &elements); - TclNRAddCallback(interp, TEOEx_ByteCodeCallback, savedVarFramePtr, - objPtr, INT2PTR(allowExceptions), NULL); - return TclNRExecuteByteCode(interp, codePtr); - } + 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). + * We're not supposed to use the compiler or byte-code interpreter. + * Let Tcl_EvalEx evaluate the command directly (and probably more + * slowly). */ - const char *script; - Tcl_Size numSrcBytes; + /* + * 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 @@ -6711,7 +5189,7 @@ TclNREvalObjEx( * 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" + * 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 @@ -6719,92 +5197,133 @@ TclNREvalObjEx( * executing nested commands in the eval/direct path. */ - ContLineLoc *saveCLLocPtr = iPtr->scriptCLLocPtr; + ContLineLoc* saveCLLocPtr = iPtr->scriptCLLocPtr; + ContLineLoc* clLocPtr = TclContinuationsGet (objPtr); - assert(invoker == NULL); + if (clLocPtr) { + iPtr->scriptCLLocPtr = clLocPtr; + Tcl_Preserve (iPtr->scriptCLLocPtr); + } else { + iPtr->scriptCLLocPtr = NULL; + } - iPtr->scriptCLLocPtr = TclContinuationsGet(objPtr); + if (invoker == NULL) { + /* + * No context, force opening of our own. + */ - Tcl_IncrRefCount(objPtr); + 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 = TclGetStringFromObj(objPtr, &numSrcBytes); - result = Tcl_EvalEx(interp, script, numSrcBytes, flags); + int pc = 0; + CmdFrame *ctxPtr = (CmdFrame *) + TclStackAlloc(interp, sizeof(CmdFrame)); - TclDecrRefCount(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. + */ - iPtr->scriptCLLocPtr = saveCLLocPtr; - return result; - } -} + TclGetSrcInfoForPc(ctxPtr); + pc = 1; + } -static int -TEOEx_ByteCodeCallback( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - CallFrame *savedVarFramePtr = (CallFrame *)data[0]; - Tcl_Obj *objPtr = (Tcl_Obj *)data[1]; - int allowExceptions = PTR2INT(data[2]); + script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); - if (iPtr->numLevels == 0) { - if (result == TCL_RETURN) { - result = TclUpdateReturnInfo(iPtr); - } - if ((result != TCL_OK) && (result != TCL_ERROR) && !allowExceptions) { - const char *script; - Tcl_Size numSrcBytes; - - ProcessUnexpectedResult(interp, result); - result = TCL_ERROR; - script = TclGetStringFromObj(objPtr, &numSrcBytes); - Tcl_LogCommandInfo(interp, script, script, numSrcBytes); + if ((ctxPtr->nline <= word) || + (ctxPtr->line[word] < 0) || + (ctxPtr->type != TCL_LOCATION_SOURCE)) { + /* + * Dynamic script, or dynamic context, force our own + * context. + */ + + result = Tcl_EvalEx(interp, script, numSrcBytes, flags); + + } else { + /* + * Absolute context to reuse. + */ + + iPtr->invokeCmdFramePtr = ctxPtr; + iPtr->evalFlags |= TCL_EVAL_CTX; + + result = TclEvalEx(interp, script, numSrcBytes, flags, + ctxPtr->line[word], NULL, script); + } + + if (pc && (ctxPtr->type == TCL_LOCATION_SOURCE)) { + /* + * Death of SrcInfo reference. + */ + + Tcl_DecrRefCount(ctxPtr->data.eval.path); + } + TclStackFree(interp, ctxPtr); } /* - * We are returning to level 0, so should call TclResetCancellation. - * Let us just unset the flags inline. + * Now release the lock on the continuation line information, if + * any, and restore the caller's settings. */ - TclUnsetCancelFlags(iPtr); - } - iPtr->evalFlags = 0; - - /* - * Restore the callFrame if this was a TCL_EVAL_GLOBAL. - */ - - if (savedVarFramePtr) { - iPtr->varFramePtr = savedVarFramePtr; - } + 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. + */ + int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); - TclDecrRefCount(objPtr); - return result; -} + savedVarFramePtr = iPtr->varFramePtr; + if (flags & TCL_EVAL_GLOBAL) { + iPtr->varFramePtr = iPtr->rootFramePtr; + } -static int -TEOEx_ListCallback( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - Tcl_Obj *listPtr = (Tcl_Obj *)data[0]; - CmdFrame *eoFramePtr = (CmdFrame *)data[1]; - Tcl_Obj *objPtr = (Tcl_Obj *)data[2]; + result = TclCompEvalObj(interp, objPtr, invoker, word); - /* - * Remove the cmdFrame - */ + /* + * If we are again at the top level, process any unusual return code + * returned by the evaluated code. + */ - if (eoFramePtr) { - iPtr->cmdFramePtr = eoFramePtr->nextPtr; - TclStackFree(interp, eoFramePtr); + if (iPtr->numLevels == 0) { + if (result == TCL_RETURN) { + result = TclUpdateReturnInfo(iPtr); + } + if ((result != TCL_OK) && (result != TCL_ERROR) + && !allowExceptions) { + ProcessUnexpectedResult(interp, result); + result = TCL_ERROR; + script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); + Tcl_LogCommandInfo(interp, script, script, numSrcBytes); + } + } + iPtr->evalFlags = 0; + iPtr->varFramePtr = savedVarFramePtr; } - TclDecrRefCount(objPtr); - TclDecrRefCount(listPtr); + TclDecrRefCount(objPtr); return result; } @@ -6834,21 +5353,17 @@ ProcessUnexpectedResult( * result code was returned. */ int returnCode) /* The unexpected result code. */ { - char buf[TCL_INTEGER_SPACE]; - Tcl_ResetResult(interp); if (returnCode == TCL_BREAK) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invoked \"break\" outside of a loop", TCL_INDEX_NONE)); + Tcl_AppendResult(interp, + "invoked \"break\" outside of a loop", NULL); } else if (returnCode == TCL_CONTINUE) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "invoked \"continue\" outside of a loop", TCL_INDEX_NONE)); + Tcl_AppendResult(interp, + "invoked \"continue\" outside of a loop", NULL); } else { Tcl_SetObjResult(interp, Tcl_ObjPrintf( "command returned bad code: %d", returnCode)); } - snprintf(buf, sizeof(buf), "%d", returnCode); - Tcl_SetErrorCode(interp, "TCL", "UNEXPECTED_RESULT_CODE", buf, (void *)NULL); } /* @@ -6879,7 +5394,7 @@ Tcl_ExprLong( const char *exprstring, /* Expression to evaluate. */ long *ptr) /* Where to store result. */ { - Tcl_Obj *exprPtr; + register Tcl_Obj *exprPtr; int result = TCL_OK; if (*exprstring == '\0') { /* @@ -6888,7 +5403,7 @@ Tcl_ExprLong( *ptr = 0; } else { - exprPtr = Tcl_NewStringObj(exprstring, TCL_INDEX_NONE); + exprPtr = Tcl_NewStringObj(exprstring, -1); Tcl_IncrRefCount(exprPtr); result = Tcl_ExprLongObj(interp, exprPtr, ptr); Tcl_DecrRefCount(exprPtr); @@ -6906,7 +5421,7 @@ Tcl_ExprDouble( const char *exprstring, /* Expression to evaluate. */ double *ptr) /* Where to store result. */ { - Tcl_Obj *exprPtr; + register Tcl_Obj *exprPtr; int result = TCL_OK; if (*exprstring == '\0') { @@ -6916,7 +5431,7 @@ Tcl_ExprDouble( *ptr = 0.0; } else { - exprPtr = Tcl_NewStringObj(exprstring, TCL_INDEX_NONE); + exprPtr = Tcl_NewStringObj(exprstring, -1); Tcl_IncrRefCount(exprPtr); result = Tcl_ExprDoubleObj(interp, exprPtr, ptr); Tcl_DecrRefCount(exprPtr); @@ -6944,7 +5459,7 @@ Tcl_ExprBoolean( return TCL_OK; } else { int result; - Tcl_Obj *exprPtr = Tcl_NewStringObj(exprstring, TCL_INDEX_NONE); + Tcl_Obj *exprPtr = Tcl_NewStringObj(exprstring, -1); Tcl_IncrRefCount(exprPtr); result = Tcl_ExprBooleanObj(interp, exprPtr, ptr); @@ -6986,20 +5501,20 @@ int Tcl_ExprLongObj( Tcl_Interp *interp, /* Context in which to evaluate the * expression. */ - Tcl_Obj *objPtr, /* Expression to evaluate. */ + register Tcl_Obj *objPtr, /* Expression to evaluate. */ long *ptr) /* Where to store long result. */ { Tcl_Obj *resultPtr; int result, type; double d; - void *internalPtr; + ClientData internalPtr; result = Tcl_ExprObj(interp, objPtr, &resultPtr); if (result != TCL_OK) { return TCL_ERROR; } - if (Tcl_GetNumberFromObj(interp, resultPtr, &internalPtr, &type)!=TCL_OK) { + if (TclGetNumberFromObj(interp, resultPtr, &internalPtr, &type) != TCL_OK){ return TCL_ERROR; } @@ -7013,9 +5528,10 @@ Tcl_ExprLongObj( return TCL_ERROR; } resultPtr = Tcl_NewBignumObj(&big); + /* FALLTHROUGH */ } - /* FALLTHRU */ - case TCL_NUMBER_INT: + case TCL_NUMBER_LONG: + case TCL_NUMBER_WIDE: case TCL_NUMBER_BIG: result = TclGetLongFromObj(interp, resultPtr, ptr); break; @@ -7033,19 +5549,19 @@ int Tcl_ExprDoubleObj( Tcl_Interp *interp, /* Context in which to evaluate the * expression. */ - Tcl_Obj *objPtr, /* Expression to evaluate. */ + register Tcl_Obj *objPtr, /* Expression to evaluate. */ double *ptr) /* Where to store double result. */ { Tcl_Obj *resultPtr; int result, type; - void *internalPtr; + ClientData internalPtr; result = Tcl_ExprObj(interp, objPtr, &resultPtr); if (result != TCL_OK) { return TCL_ERROR; } - result = Tcl_GetNumberFromObj(interp, resultPtr, &internalPtr, &type); + result = TclGetNumberFromObj(interp, resultPtr, &internalPtr, &type); if (result == TCL_OK) { switch (type) { case TCL_NUMBER_NAN: @@ -7069,7 +5585,7 @@ int Tcl_ExprBooleanObj( Tcl_Interp *interp, /* Context in which to evaluate the * expression. */ - Tcl_Obj *objPtr, /* Expression to evaluate. */ + register Tcl_Obj *objPtr, /* Expression to evaluate. */ int *ptr) /* Where to store 0/1 result. */ { Tcl_Obj *resultPtr; @@ -7091,7 +5607,6 @@ Tcl_ExprBooleanObj( * * 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 or namespace, thus it cannot see any current state on the * stack of that interpreter. @@ -7109,7 +5624,7 @@ int TclObjInvokeNamespace( Tcl_Interp *interp, /* Interpreter in which command is to be * invoked. */ - Tcl_Size objc, /* Count of arguments. */ + int objc, /* Count of arguments. */ Tcl_Obj *const objv[], /* Argument objects; objv[0] points to the * name of the command to invoke. */ Tcl_Namespace *nsPtr, /* The namespace to use. */ @@ -7125,7 +5640,11 @@ TclObjInvokeNamespace( * command. */ - (void) TclPushStackFrame(interp, &framePtr, nsPtr, /*isProcFrame*/0); + result = TclPushStackFrame(interp, &framePtr, nsPtr, /*isProcFrame*/0); + if (result != TCL_OK) { + return TCL_ERROR; + } + result = TclObjInvoke(interp, objc, objv, flags); TclPopStackFrame(interp); @@ -7153,39 +5672,36 @@ int TclObjInvoke( Tcl_Interp *interp, /* Interpreter in which command is to be * invoked. */ - Tcl_Size objc, /* Count of arguments. */ + 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. */ { + register Interp *iPtr = (Interp *) interp; + Tcl_HashTable *hTblPtr; /* Table of hidden commands. */ + char *cmdName; /* Name of the command from objv[0]. */ + Tcl_HashEntry *hPtr = NULL; + Command *cmdPtr; + int result; + if (interp == NULL) { return TCL_ERROR; } + if ((objc < 1) || (objv == NULL)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "illegal argument vector", TCL_INDEX_NONE)); + Tcl_AppendResult(interp, "illegal argument vector", NULL); return TCL_ERROR; } + if ((flags & TCL_INVOKE_HIDDEN) == 0) { Tcl_Panic("TclObjInvoke: called without TCL_INVOKE_HIDDEN"); } - return Tcl_NRCallObjProc(interp, TclNRInvoke, NULL, objc, objv); -} -int -TclNRInvoke( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - Tcl_HashTable *hTblPtr; /* Table of hidden commands. */ - const char *cmdName; /* Name of the command from objv[0]. */ - Tcl_HashEntry *hPtr = NULL; - Command *cmdPtr; + if (TclInterpReady(interp) == TCL_ERROR) { + return TCL_ERROR; + } cmdName = TclGetString(objv[0]); hTblPtr = iPtr->hiddenCmdTablePtr; @@ -7193,39 +5709,37 @@ TclNRInvoke( hPtr = Tcl_FindHashEntry(hTblPtr, cmdName); } if (hPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "invalid hidden command name \"%s\"", cmdName)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "HIDDENTOKEN", cmdName, - (void *)NULL); + Tcl_AppendResult(interp, "invalid hidden command name \"", + cmdName, "\"", NULL); return TCL_ERROR; } - cmdPtr = (Command *)Tcl_GetHashValue(hPtr); + cmdPtr = Tcl_GetHashValue(hPtr); /* - * Avoid the exception-handling brain damage when numLevels == 0 + * Invoke the command function. */ - iPtr->numLevels++; - Tcl_NRAddCallback(interp, NRPostInvoke, NULL, NULL, NULL, NULL); + iPtr->cmdCount++; + result = cmdPtr->objProc(cmdPtr->objClientData, interp, objc, objv); /* - * Normal command resolution of objv[0] isn't going to find cmdPtr. - * That's the whole point of **hidden** commands. So tell the Eval core - * machinery not to even try (and risk finding something wrong). + * If an error occurred, record information about what was being executed + * when the error occurred. */ - return TclNREvalObjv(interp, objc, objv, TCL_EVAL_NORESOLVE, cmdPtr); -} - -static int -NRPostInvoke( - TCL_UNUSED(void **), - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *)interp; + if ((result == TCL_ERROR) + && ((flags & TCL_INVOKE_NO_TRACEBACK) == 0) + && ((iPtr->flags & ERR_ALREADY_LOGGED) == 0)) { + int length; + Tcl_Obj *command = Tcl_NewListObj(objc, objv); + const char *cmdString; - iPtr->numLevels--; + Tcl_IncrRefCount(command); + cmdString = Tcl_GetStringFromObj(command, &length); + Tcl_LogCommandInfo(interp, cmdString, cmdString, length); + Tcl_DecrRefCount(command); + iPtr->flags &= ~ERR_ALREADY_LOGGED; + } return result; } @@ -7262,9 +5776,9 @@ Tcl_ExprString( * An empty string. Just set the interpreter's result to 0. */ - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(0)); + Tcl_SetResult(interp, "0", TCL_VOLATILE); } else { - Tcl_Obj *resultPtr, *exprObj = Tcl_NewStringObj(expr, TCL_INDEX_NONE); + Tcl_Obj *resultPtr, *exprObj = Tcl_NewStringObj(expr, -1); Tcl_IncrRefCount(exprObj); code = Tcl_ExprObj(interp, exprObj, &resultPtr); @@ -7273,13 +5787,13 @@ Tcl_ExprString( Tcl_SetObjResult(interp, resultPtr); Tcl_DecrRefCount(resultPtr); } - } - /* - * Force the string rep of the interp result. - */ + /* + * Force the string rep of the interp result. + */ - (void) Tcl_GetStringResult(interp); + (void) Tcl_GetStringResult(interp); + } return code; } @@ -7302,14 +5816,13 @@ Tcl_ExprString( *---------------------------------------------------------------------- */ -#undef Tcl_AddObjErrorInfo void Tcl_AppendObjToErrorInfo( Tcl_Interp *interp, /* Interpreter to which error information * pertains. */ Tcl_Obj *objPtr) /* Message to record. */ { - Tcl_Size length; + int length; const char *message = TclGetStringFromObj(objPtr, &length); Tcl_IncrRefCount(objPtr); @@ -7336,8 +5849,6 @@ Tcl_AppendObjToErrorInfo( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED -#undef Tcl_AddErrorInfo void Tcl_AddErrorInfo( Tcl_Interp *interp, /* Interpreter to which error information @@ -7346,7 +5857,6 @@ Tcl_AddErrorInfo( { Tcl_AddObjErrorInfo(interp, message, -1); } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -7375,10 +5885,10 @@ Tcl_AddObjErrorInfo( * pertains. */ const char *message, /* Points to the first byte of an array of * bytes of the message. */ - Tcl_Size length) /* The number of bytes in the message. If < 0, + int length) /* The number of bytes in the message. If < 0, * then append all bytes up to a NULL byte. */ { - Interp *iPtr = (Interp *) interp; + register Interp *iPtr = (Interp *) interp; /* * If we are just starting to log an error, errorInfo is initialized from @@ -7387,8 +5897,7 @@ Tcl_AddObjErrorInfo( iPtr->flags |= ERR_LEGACY_COPY; if (iPtr->errorInfo == NULL) { -#if !defined(TCL_NO_DEPRECATED) && TCL_MAJOR_VERSION < 9 - if (*(iPtr->result) != 0) { + 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 @@ -7397,13 +5906,13 @@ Tcl_AddObjErrorInfo( * interp->result completely. */ - iPtr->errorInfo = Tcl_NewStringObj(iPtr->result, TCL_INDEX_NONE); - } else -#endif /* !defined(TCL_NO_DEPRECATED) */ + iPtr->errorInfo = Tcl_NewStringObj(interp->result, -1); + } else { iPtr->errorInfo = iPtr->objResultPtr; + } Tcl_IncrRefCount(iPtr->errorInfo); if (!iPtr->errorCode) { - Tcl_SetErrorCode(interp, "NONE", (void *)NULL); + Tcl_SetErrorCode(interp, "NONE", NULL); } } @@ -7422,7 +5931,7 @@ Tcl_AddObjErrorInfo( } /* - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- * * Tcl_VarEvalVA -- * @@ -7431,17 +5940,17 @@ Tcl_AddObjErrorInfo( * * Results: * A standard Tcl return result. An error message or other result may be - * left in the interp. + * left in the interp's result. * * Side effects: * Depends on what was done by the command. * - *---------------------------------------------------------------------- + *--------------------------------------------------------------------------- */ int Tcl_VarEvalVA( - Tcl_Interp *interp, /* Interpreter in which to evaluate command */ + Tcl_Interp *interp, /* Interpreter in which to evaluate command. */ va_list argList) /* Variable argument list. */ { Tcl_DString buf; @@ -7460,10 +5969,10 @@ Tcl_VarEvalVA( if (string == NULL) { break; } - Tcl_DStringAppend(&buf, string, TCL_INDEX_NONE); + Tcl_DStringAppend(&buf, string, -1); } - result = Tcl_EvalEx(interp, Tcl_DStringValue(&buf), TCL_INDEX_NONE, 0); + result = Tcl_Eval(interp, Tcl_DStringValue(&buf)); Tcl_DStringFree(&buf); return result; } @@ -7478,14 +5987,14 @@ Tcl_VarEvalVA( * * Results: * A standard Tcl return result. An error message or other result may be - * left in the interp. + * left in interp->result. * * Side effects: * Depends on what was done by the command. * *---------------------------------------------------------------------- */ - + /* ARGSUSED */ int Tcl_VarEval( Tcl_Interp *interp, @@ -7520,25 +6029,21 @@ Tcl_VarEval( *---------------------------------------------------------------------- */ -#ifndef TCL_NO_DEPRECATED -#undef Tcl_GlobalEval int Tcl_GlobalEval( - Tcl_Interp *interp, /* Interpreter in which to evaluate - * command. */ + Tcl_Interp *interp, /* Interpreter in which to evaluate command. */ const char *command) /* Command to evaluate. */ { - Interp *iPtr = (Interp *) interp; + register Interp *iPtr = (Interp *) interp; int result; CallFrame *savedVarFramePtr; savedVarFramePtr = iPtr->varFramePtr; iPtr->varFramePtr = iPtr->rootFramePtr; - result = Tcl_EvalEx(interp, command, TCL_INDEX_NONE, 0); + result = Tcl_Eval(interp, command); iPtr->varFramePtr = savedVarFramePtr; return result; } -#endif /* TCL_NO_DEPRECATED */ /* *---------------------------------------------------------------------- @@ -7557,14 +6062,14 @@ Tcl_GlobalEval( *---------------------------------------------------------------------- */ -Tcl_Size +int Tcl_SetRecursionLimit( Tcl_Interp *interp, /* Interpreter whose nesting limit is to be * set. */ - Tcl_Size depth) /* New value for maximum depth. */ + int depth) /* New value for maximimum depth. */ { Interp *iPtr = (Interp *) interp; - Tcl_Size old; + int old; old = iPtr->maxNestingDepth; if (depth > 0) { @@ -7660,7 +6165,7 @@ Tcl_GetVersion( static int ExprCeilFunc( - TCL_UNUSED(void *), + ClientData clientData, /* Ignored */ Tcl_Interp *interp, /* The interpreter in which to execute the * function. */ int objc, /* Actual parameter count. */ @@ -7676,19 +6181,14 @@ ExprCeilFunc( } code = Tcl_GetDoubleFromObj(interp, objv[1], &d); #ifdef ACCEPT_NAN - if (code != TCL_OK) { - const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objv[1], &tclDoubleType); - - if (irPtr) { - Tcl_SetObjResult(interp, objv[1]); - return TCL_OK; - } + 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); @@ -7700,7 +6200,7 @@ ExprCeilFunc( static int ExprFloorFunc( - TCL_UNUSED(void *), + ClientData clientData, /* Ignored */ Tcl_Interp *interp, /* The interpreter in which to execute the * function. */ int objc, /* Actual parameter count. */ @@ -7716,19 +6216,14 @@ ExprFloorFunc( } code = Tcl_GetDoubleFromObj(interp, objv[1], &d); #ifdef ACCEPT_NAN - if (code != TCL_OK) { - const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objv[1], &tclDoubleType); - - if (irPtr) { - Tcl_SetObjResult(interp, objv[1]); - return TCL_OK; - } + 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); @@ -7740,18 +6235,19 @@ ExprFloorFunc( static int ExprIsqrtFunc( - TCL_UNUSED(void *), + ClientData clientData, /* Ignored */ Tcl_Interp *interp, /* The interpreter in which to execute. */ int objc, /* Actual parameter count. */ Tcl_Obj *const *objv) /* Actual parameter list. */ { - void *ptr; + 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. */ + int exact = 0; /* Flag == 1 if the argument can be + * represented in a double as an exact + * integer. */ /* * Check syntax. @@ -7766,7 +6262,7 @@ ExprIsqrtFunc( * Make sure that the arg is a number. */ - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { return TCL_ERROR; } @@ -7794,13 +6290,13 @@ ExprIsqrtFunc( if (Tcl_GetBignumFromObj(interp, objv[1], &big) != TCL_OK) { return TCL_ERROR; } - if (mp_isneg(&big)) { + if (SIGN(&big) == MP_NEG) { mp_clear(&big); goto negarg; } break; default: - if (TclGetWideIntFromObj(interp, objv[1], &w) != TCL_OK) { + if (Tcl_GetWideIntFromObj(interp, objv[1], &w) != TCL_OK) { return TCL_ERROR; } if (w < 0) { @@ -7822,31 +6318,24 @@ ExprIsqrtFunc( Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt) sqrt(d))); } else { mp_int root; - mp_err err; - err = mp_init(&root); - if (err == MP_OKAY) { - err = mp_sqrt(&big, &root); - } + mp_init(&root); + mp_sqrt(&big, &root); mp_clear(&big); - if (err != MP_OKAY) { - return TCL_ERROR; - } Tcl_SetObjResult(interp, Tcl_NewBignumObj(&root)); } + return TCL_OK; negarg: - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "square root of negative argument", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", - "domain error: argument not in valid range", (void *)NULL); + Tcl_SetObjResult(interp, + Tcl_NewStringObj("square root of negative argument", -1)); return TCL_ERROR; } static int ExprSqrtFunc( - TCL_UNUSED(void *), + ClientData clientData, /* Ignored */ Tcl_Interp *interp, /* The interpreter in which to execute the * function. */ int objc, /* Actual parameter count. */ @@ -7862,32 +6351,21 @@ ExprSqrtFunc( } code = Tcl_GetDoubleFromObj(interp, objv[1], &d); #ifdef ACCEPT_NAN - if (code != TCL_OK) { - const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objv[1], &tclDoubleType); - - if (irPtr) { - Tcl_SetObjResult(interp, objv[1]); - return TCL_OK; - } + 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) && isinf(d) + if ((d >= 0.0) && TclIsInfinite(d) && (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK)) { mp_int root; - mp_err err; - err = mp_init(&root); - if (err == MP_OKAY) { - err = mp_sqrt(&big, &root); - } + mp_init(&root); + mp_sqrt(&big, &root); mp_clear(&big); - if (err != MP_OKAY) { - mp_clear(&root); - return TCL_ERROR; - } Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclBignumToDouble(&root))); mp_clear(&root); } else { @@ -7898,7 +6376,7 @@ ExprSqrtFunc( static int ExprUnaryFunc( - void *clientData, /* Contains the address of a function that + 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 @@ -7916,21 +6394,17 @@ ExprUnaryFunc( } code = Tcl_GetDoubleFromObj(interp, objv[1], &d); #ifdef ACCEPT_NAN - if (code != TCL_OK) { - const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objv[1], &tclDoubleType); - - if (irPtr) { - d = irPtr->doubleValue; - Tcl_ResetResult(interp); - code = TCL_OK; - } + 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)); + return CheckDoubleResult(interp, (*func)(d)); } static int @@ -7939,12 +6413,12 @@ CheckDoubleResult( double dResult) { #ifndef ACCEPT_NAN - if (isnan(dResult)) { + if (TclIsNaN(dResult)) { TclExprFloatError(interp, dResult); return TCL_ERROR; } #endif - if ((errno == ERANGE) && ((dResult == 0.0) || isinf(dResult))) { + if ((errno == ERANGE) && ((dResult == 0.0) || TclIsInfinite(dResult))) { /* * When ERANGE signals under/overflow, just accept 0.0 or +/-Inf */ @@ -7962,7 +6436,7 @@ CheckDoubleResult( static int ExprBinaryFunc( - void *clientData, /* Contains the address of a function that + 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 @@ -7980,14 +6454,10 @@ ExprBinaryFunc( } code = Tcl_GetDoubleFromObj(interp, objv[1], &d1); #ifdef ACCEPT_NAN - if (code != TCL_OK) { - const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objv[1], &tclDoubleType); - - if (irPtr) { - d1 = irPtr->doubleValue; - Tcl_ResetResult(interp); - code = TCL_OK; - } + if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { + d1 = objv[1]->internalRep.doubleValue; + Tcl_ResetResult(interp); + code = TCL_OK; } #endif if (code != TCL_OK) { @@ -7995,32 +6465,28 @@ ExprBinaryFunc( } code = Tcl_GetDoubleFromObj(interp, objv[2], &d2); #ifdef ACCEPT_NAN - if (code != TCL_OK) { - const Tcl_ObjInternalRep *irPtr = TclFetchInternalRep(objv[1], &tclDoubleType); - - if (irPtr) { - d2 = irPtr->doubleValue; - Tcl_ResetResult(interp); - code = TCL_OK; - } + 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)); + return CheckDoubleResult(interp, (*func)(d1, d2)); } static int ExprAbsFunc( - TCL_UNUSED(void *), + 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. */ { - void *ptr; + ClientData ptr; int type; mp_int big; @@ -8029,45 +6495,32 @@ ExprAbsFunc( return TCL_ERROR; } - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { return TCL_ERROR; } - if (type == TCL_NUMBER_INT) { - Tcl_WideInt l = *((const Tcl_WideInt *) ptr); + if (type == TCL_NUMBER_LONG) { + long l = *((const long *) ptr); - if (l > 0) { + if (l > (long)0) { goto unChanged; - } else if (l == 0) { - if (TclHasStringRep(objv[1])) { - Tcl_Size numBytes; - const char *bytes = TclGetStringFromObj(objv[1], &numBytes); - - while (numBytes) { - if (*bytes == '-') { - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(0)); + } 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; } - bytes++; numBytes--; + string++; } } goto unChanged; - } else if (l == WIDE_MIN) { - if (sizeof(Tcl_WideInt) > sizeof(int64_t)) { - Tcl_WideUInt ul = -(Tcl_WideUInt)WIDE_MIN; - if (mp_init(&big) != MP_OKAY || mp_unpack(&big, 1, 1, - sizeof(Tcl_WideInt), 0, 0, &ul) != MP_OKAY) { - return TCL_ERROR; - } - if (mp_neg(&big, &big) != MP_OKAY) { - return TCL_ERROR; - } - } else if (mp_init_i64(&big, l) != MP_OKAY) { - return TCL_ERROR; - } + } else if (l == LONG_MIN) { + TclBNInitBignumFromLong(&big, l); goto tooLarge; } - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(-l)); + Tcl_SetObjResult(interp, Tcl_NewLongObj(-l)); return TCL_OK; } @@ -8075,29 +6528,44 @@ ExprAbsFunc( double d = *((const double *) ptr); static const double poszero = 0.0; - /* - * We need to distinguish here between positive 0.0 and negative -0.0. - * [Bug 2954959] + /* 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))) { + if (!memcmp(&d, &poszero, sizeof(double))) { + goto unChanged; + } + } else { + if (d > -0.0) { 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) { - if (mp_isneg((const mp_int *) ptr)) { + /* TODO: const correctness ? */ + if (mp_cmp_d((mp_int *) ptr, 0) == MP_LT) { Tcl_GetBignumFromObj(NULL, objv[1], &big); tooLarge: - if (mp_neg(&big, &big) != MP_OKAY) { - return TCL_ERROR; - } + mp_neg(&big, &big); Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); } else { unChanged: @@ -8112,7 +6580,6 @@ ExprAbsFunc( return TCL_OK; #else double d; - Tcl_GetDoubleFromObj(interp, objv[1], &d); return TCL_ERROR; #endif @@ -8122,7 +6589,7 @@ ExprAbsFunc( static int ExprBoolFunc( - TCL_UNUSED(void *), + ClientData clientData, /* Ignored. */ Tcl_Interp *interp, /* The interpreter in which to execute the * function. */ int objc, /* Actual parameter count. */ @@ -8143,21 +6610,20 @@ ExprBoolFunc( static int ExprDoubleFunc( - TCL_UNUSED(void *), + 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 (TclHasInternalRep(objv[1], &tclDoubleType)) { + if (objv[1]->typePtr == &tclDoubleType) { Tcl_SetObjResult(interp, objv[1]); return TCL_OK; } @@ -8169,8 +6635,8 @@ ExprDoubleFunc( } static int -ExprIntFunc( - TCL_UNUSED(void *), +ExprEntierFunc( + ClientData clientData, /* Ignored. */ Tcl_Interp *interp, /* The interpreter in which to execute the * function. */ int objc, /* Actual parameter count. */ @@ -8178,19 +6644,19 @@ ExprIntFunc( { double d; int type; - void *ptr; + ClientData ptr; if (objc != 2) { MathFuncWrongNumArgs(interp, 2, objc, objv); return TCL_ERROR; } - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { return TCL_ERROR; } if (type == TCL_NUMBER_DOUBLE) { d = *((const double *) ptr); - if ((d >= (double)WIDE_MAX) || (d <= (double)WIDE_MIN)) { + if ((d >= (double)LONG_MAX) || (d <= (double)LONG_MIN)) { mp_int big; if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { @@ -8200,9 +6666,9 @@ ExprIntFunc( Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); return TCL_OK; } else { - Tcl_WideInt result = (Tcl_WideInt) d; + long result = (long) d; - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(result)); + Tcl_SetObjResult(interp, Tcl_NewLongObj(result)); return TCL_OK; } } @@ -8225,92 +6691,72 @@ ExprIntFunc( } static int -ExprWideFunc( - TCL_UNUSED(void *), +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. */ { - Tcl_WideInt wResult; - - if (ExprIntFunc(NULL, interp, objc, objv) != TCL_OK) { + long iResult; + Tcl_Obj *objPtr; + if (ExprEntierFunc(NULL, interp, objc, objv) != TCL_OK) { return TCL_ERROR; } - TclGetWideBitsFromObj(NULL, Tcl_GetObjResult(interp), &wResult); - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(wResult)); - return TCL_OK; -} + objPtr = Tcl_GetObjResult(interp); + if (TclGetLongFromObj(NULL, objPtr, &iResult) != TCL_OK) { + /* + * Truncate the bignum; keep only bits in long range. + */ -/* - * Common implmentation of max() and min(). - */ -static int -ExprMaxMinFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count. */ - Tcl_Obj *const *objv, /* Actual parameter vector. */ - int op) /* Comparison direction */ -{ - Tcl_Obj *res; - double d; - int type; - int i; - void *ptr; + mp_int big; - if (objc < 2) { - MathFuncWrongNumArgs(interp, 2, objc, objv); - return TCL_ERROR; + 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); } - res = objv[1]; - for (i = 1; i < objc; i++) { - if (Tcl_GetNumberFromObj(interp, objv[i], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type == TCL_NUMBER_NAN) { - /* - * Get the error message for NaN. - */ - - Tcl_GetDoubleFromObj(interp, objv[i], &d); - return TCL_ERROR; - } - if (TclCompareTwoNumbers(objv[i], res) == op) { - res = objv[i]; - } - } - - Tcl_SetObjResult(interp, res); + Tcl_SetObjResult(interp, Tcl_NewLongObj(iResult)); return TCL_OK; } static int -ExprMaxFunc( - TCL_UNUSED(void *), +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. */ { - return ExprMaxMinFunc(NULL, interp, objc, objv, MP_GT); -} + 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. + */ -static int -ExprMinFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count. */ - Tcl_Obj *const *objv) /* Actual parameter vector. */ -{ - return ExprMaxMinFunc(NULL, interp, objc, objv, MP_LT); + 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( - TCL_UNUSED(void *), + ClientData clientData, /* Ignored. */ Tcl_Interp *interp, /* The interpreter in which to execute the * function. */ int objc, /* Actual parameter count. */ @@ -8331,19 +6777,19 @@ ExprRandFunc( iPtr->flags |= RAND_SEED_INITIALIZED; /* - * To ensure different seeds in different threads (bug #416643), - * take into consideration the thread this interp is running in. + * Take into consideration the thread this interp is running in order + * to insure different seeds in different threads (bug #416643) */ - iPtr->randSeed = TclpGetClicks() + PTR2UINT(Tcl_GetCurrentThread())*4093U; + iPtr->randSeed = TclpGetClicks() + (PTR2INT(Tcl_GetCurrentThread())<<12); /* * Make sure 1 <= randSeed <= (2^31) - 2. See below. */ - iPtr->randSeed &= 0x7FFFFFFFL; - if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7FFFFFFFL)) { - iPtr->randSeed ^= 123459876L; + iPtr->randSeed &= (unsigned long) 0x7fffffff; + if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { + iPtr->randSeed ^= 123459876; } } @@ -8403,14 +6849,14 @@ ExprRandFunc( static int ExprRoundFunc( - TCL_UNUSED(void *), + 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; - void *ptr; + ClientData ptr; int type; if (objc != 2) { @@ -8418,13 +6864,13 @@ ExprRoundFunc( return TCL_ERROR; } - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { + if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { return TCL_ERROR; } if (type == TCL_NUMBER_DOUBLE) { double fractPart, intPart; - Tcl_WideInt max = WIDE_MAX, min = WIDE_MIN; + long max = LONG_MAX, min = LONG_MIN; fractPart = modf(*((const double *) ptr), &intPart); if (fractPart <= -0.5) { @@ -8434,31 +6880,27 @@ ExprRoundFunc( } if ((intPart >= (double)max) || (intPart <= (double)min)) { mp_int big; - mp_err err = MP_OKAY; if (Tcl_InitBignumFromDouble(interp, intPart, &big) != TCL_OK) { /* Infinity */ return TCL_ERROR; } if (fractPart <= -0.5) { - err = mp_sub_d(&big, 1, &big); + mp_sub_d(&big, 1, &big); } else if (fractPart >= 0.5) { - err = mp_add_d(&big, 1, &big); - } - if (err != MP_OKAY) { - return TCL_ERROR; + mp_add_d(&big, 1, &big); } Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); return TCL_OK; } else { - Tcl_WideInt result = (Tcl_WideInt)intPart; + long result = (long)intPart; if (fractPart <= -0.5) { result--; } else if (fractPart >= 0.5) { result++; } - Tcl_SetObjResult(interp, Tcl_NewWideIntObj(result)); + Tcl_SetObjResult(interp, Tcl_NewLongObj(result)); return TCL_OK; } } @@ -8482,14 +6924,14 @@ ExprRoundFunc( static int ExprSrandFunc( - TCL_UNUSED(void *), + 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; - Tcl_WideInt w = 0; /* Initialized to avoid compiler warning. */ + long i = 0; /* Initialized to avoid compiler warning. */ /* * Convert argument and use it to reset the seed. @@ -8500,18 +6942,31 @@ ExprSrandFunc( return TCL_ERROR; } - if (TclGetWideBitsFromObj(NULL, objv[1], &w) != TCL_OK) { - 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. + * ExprRandFunc() for more details. */ iPtr->flags |= RAND_SEED_INITIALIZED; - iPtr->randSeed = (long) w & 0x7FFFFFFF; - if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7FFFFFFF)) { + iPtr->randSeed = i; + iPtr->randSeed &= (unsigned long) 0x7fffffff; + if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { iPtr->randSeed ^= 123459876; } @@ -8521,396 +6976,7 @@ ExprSrandFunc( * will always succeed. */ - return ExprRandFunc(NULL, interp, 1, objv); -} - -/* - *---------------------------------------------------------------------- - * - * Double Classification Functions -- - * - * This page contains the functions that implement all of the built-in - * math functions for classifying IEEE doubles. - * - * These have to be a little bit careful while Tcl_GetDoubleFromObj() - * rejects NaN values, which these functions *explicitly* accept. - * - * 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. - * - *---------------------------------------------------------------------- - * - * Older MSVC is supported by Tcl, but doesn't have fpclassify(). Of course. - * But it does sometimes have _fpclass() which does almost the same job; if - * even that is absent, we grobble around directly in the platform's binary - * representation of double. - * - * The ClassifyDouble() function makes all that conform to a common API - * (effectively the C99 standard API renamed), and just delegates to the - * standard macro on platforms that do it correctly. - */ - -static inline int -ClassifyDouble( - double d) -{ -#if TCL_FPCLASSIFY_MODE == 0 - return fpclassify(d); -#else /* TCL_FPCLASSIFY_MODE != 0 */ - /* - * If we don't have fpclassify(), we also don't have the values it returns. - * Hence we define those here. - */ -#ifndef FP_NAN -# define FP_NAN 1 /* Value is NaN */ -# define FP_INFINITE 2 /* Value is an infinity */ -# define FP_ZERO 3 /* Value is a zero */ -# define FP_NORMAL 4 /* Value is a normal float */ -# define FP_SUBNORMAL 5 /* Value has lost accuracy */ -#endif /* !FP_NAN */ - -#if TCL_FPCLASSIFY_MODE == 3 - return __builtin_fpclassify( - FP_NAN, FP_INFINITE, FP_NORMAL, FP_SUBNORMAL, FP_ZERO, d); -#elif TCL_FPCLASSIFY_MODE == 2 - /* - * We assume this hack is only needed on little-endian systems. - * Specifically, x86 running Windows. It's fairly easy to enable for - * others if they need it (because their libc/libm is broken) but we'll - * jump that hurdle when requred. We can solve the word ordering then. - */ - - union { - double d; /* Interpret as double */ - struct { - unsigned int low; /* Lower 32 bits */ - unsigned int high; /* Upper 32 bits */ - } w; /* Interpret as unsigned integer words */ - } doubleMeaning; /* So we can look at the representation of a - * double directly. Platform (i.e., processor) - * specific; this is for x86 (and most other - * little-endian processors, but those are - * untested). */ - unsigned int exponent, mantissaLow, mantissaHigh; - /* The pieces extracted from the double. */ - int zeroMantissa; /* Was the mantissa zero? That's special. */ - - /* - * Shifts and masks to use with the doubleMeaning variable above. - */ - -#define EXPONENT_MASK 0x7FF /* 11 bits (after shifting) */ -#define EXPONENT_SHIFT 20 /* Moves exponent to bottom of word */ -#define MANTISSA_MASK 0xFFFFF /* 20 bits (plus 32 from other word) */ - - /* - * Extract the exponent (11 bits) and mantissa (52 bits). Note that we - * totally ignore the sign bit. - */ - - doubleMeaning.d = d; - exponent = (doubleMeaning.w.high >> EXPONENT_SHIFT) & EXPONENT_MASK; - mantissaLow = doubleMeaning.w.low; - mantissaHigh = doubleMeaning.w.high & MANTISSA_MASK; - zeroMantissa = (mantissaHigh == 0 && mantissaLow == 0); - - /* - * Look for the special cases of exponent. - */ - - switch (exponent) { - case 0: - /* - * When the exponent is all zeros, it's a ZERO or a SUBNORMAL. - */ - - return zeroMantissa ? FP_ZERO : FP_SUBNORMAL; - case EXPONENT_MASK: - /* - * When the exponent is all ones, it's an INF or a NAN. - */ - - return zeroMantissa ? FP_INFINITE : FP_NAN; - default: - /* - * Everything else is a NORMAL double precision float. - */ - - return FP_NORMAL; - } -#elif TCL_FPCLASSIFY_MODE == 1 - switch (_fpclass(d)) { - case _FPCLASS_NZ: - case _FPCLASS_PZ: - return FP_ZERO; - case _FPCLASS_NN: - case _FPCLASS_PN: - return FP_NORMAL; - case _FPCLASS_ND: - case _FPCLASS_PD: - return FP_SUBNORMAL; - case _FPCLASS_NINF: - case _FPCLASS_PINF: - return FP_INFINITE; - default: - Tcl_Panic("result of _fpclass() outside documented range!"); - case _FPCLASS_QNAN: - case _FPCLASS_SNAN: - return FP_NAN; - } -#else /* TCL_FPCLASSIFY_MODE not in (0..3) */ -#error "unknown or unexpected TCL_FPCLASSIFY_MODE" -#endif /* TCL_FPCLASSIFY_MODE */ -#endif /* !fpclassify */ -} - -static int -ExprIsFiniteFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - void *ptr; - int type, result = 0; - - if (objc != 2) { - MathFuncWrongNumArgs(interp, 2, objc, objv); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type != TCL_NUMBER_NAN) { - if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { - return TCL_ERROR; - } - type = ClassifyDouble(d); - result = (type != FP_INFINITE && type != FP_NAN); - } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); - return TCL_OK; -} - -static int -ExprIsInfinityFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - void *ptr; - int type, result = 0; - - if (objc != 2) { - MathFuncWrongNumArgs(interp, 2, objc, objv); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type != TCL_NUMBER_NAN) { - if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { - return TCL_ERROR; - } - result = (ClassifyDouble(d) == FP_INFINITE); - } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); - return TCL_OK; -} - -static int -ExprIsNaNFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - void *ptr; - int type, result = 1; - - if (objc != 2) { - MathFuncWrongNumArgs(interp, 2, objc, objv); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type != TCL_NUMBER_NAN) { - if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { - return TCL_ERROR; - } - result = (ClassifyDouble(d) == FP_NAN); - } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); - return TCL_OK; -} - -static int -ExprIsNormalFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - void *ptr; - int type, result = 0; - - if (objc != 2) { - MathFuncWrongNumArgs(interp, 2, objc, objv); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type != TCL_NUMBER_NAN) { - if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { - return TCL_ERROR; - } - result = (ClassifyDouble(d) == FP_NORMAL); - } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); - return TCL_OK; -} - -static int -ExprIsSubnormalFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - void *ptr; - int type, result = 0; - - if (objc != 2) { - MathFuncWrongNumArgs(interp, 2, objc, objv); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type != TCL_NUMBER_NAN) { - if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { - return TCL_ERROR; - } - result = (ClassifyDouble(d) == FP_SUBNORMAL); - } - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); - return TCL_OK; -} - -static int -ExprIsUnorderedFunc( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - void *ptr; - int type, result = 0; - - if (objc != 3) { - MathFuncWrongNumArgs(interp, 3, objc, objv); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type == TCL_NUMBER_NAN) { - result = 1; - } else { - d = *((const double *) ptr); - result = (ClassifyDouble(d) == FP_NAN); - } - - if (Tcl_GetNumberFromObj(interp, objv[2], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type == TCL_NUMBER_NAN) { - result |= 1; - } else { - d = *((const double *) ptr); - result |= (ClassifyDouble(d) == FP_NAN); - } - - Tcl_SetObjResult(interp, Tcl_NewBooleanObj(result)); - return TCL_OK; -} - -static int -FloatClassifyObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* The interpreter in which to execute the - * function. */ - int objc, /* Actual parameter count */ - Tcl_Obj *const *objv) /* Actual parameter list */ -{ - double d; - Tcl_Obj *objPtr; - void *ptr; - int type; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "floatValue"); - return TCL_ERROR; - } - - if (Tcl_GetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { - return TCL_ERROR; - } - if (type == TCL_NUMBER_NAN) { - goto gotNaN; - } else if (Tcl_GetDoubleFromObj(interp, objv[1], &d) != TCL_OK) { - return TCL_ERROR; - } - switch (ClassifyDouble(d)) { - case FP_INFINITE: - TclNewLiteralStringObj(objPtr, "infinite"); - break; - case FP_NAN: - gotNaN: - TclNewLiteralStringObj(objPtr, "nan"); - break; - case FP_NORMAL: - TclNewLiteralStringObj(objPtr, "normal"); - break; - case FP_SUBNORMAL: - TclNewLiteralStringObj(objPtr, "subnormal"); - break; - case FP_ZERO: - TclNewLiteralStringObj(objPtr, "zero"); - break; - default: - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "unable to classify number: %f", d)); - return TCL_ERROR; - } - Tcl_SetObjResult(interp, objPtr); - return TCL_OK; + return ExprRandFunc(clientData, interp, 1, objv); } /* @@ -8937,23 +7003,22 @@ MathFuncWrongNumArgs( int found, /* Actual parameter count. */ Tcl_Obj *const *objv) /* Actual parameter vector. */ { - const char *name = TclGetString(objv[0]); + const char *name = Tcl_GetString(objv[0]); const char *tail = name + strlen(name); while (tail > name+1) { - tail--; + --tail; if (*tail == ':' && tail[-1] == ':') { name = tail+1; break; } } Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "%s arguments for math function \"%s\"", - (found < expected ? "not enough" : "too many"), name)); - Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", (void *)NULL); + "too %s arguments for math function \"%s\"", + (found < expected ? "few" : "many"), name)); } - #ifdef USE_DTRACE + /* *---------------------------------------------------------------------- * @@ -8972,8 +7037,8 @@ MathFuncWrongNumArgs( static int DTraceObjCmd( - TCL_UNUSED(void *), - TCL_UNUSED(Tcl_Interp *), + ClientData dummy, /* Not used. */ + Tcl_Interp *interp, /* Current interpreter. */ int objc, /* Number of arguments. */ Tcl_Obj *const objv[]) /* Argument objects. */ { @@ -9009,1473 +7074,49 @@ DTraceObjCmd( void TclDTraceInfo( Tcl_Obj *info, - const char **args, - Tcl_Size *argsi) + char **args, + int *argsi) { - static Tcl_Obj *keys[10] = { NULL }; + static Tcl_Obj *keys[7] = { NULL }; Tcl_Obj **k = keys, *val; - int i = 0; + int i; if (!*k) { -#define kini(s) TclNewLiteralStringObj(keys[i], s); i++ - kini("cmd"); kini("type"); kini("proc"); kini("file"); - kini("method"); kini("class"); kini("lambda"); kini("object"); - kini("line"); kini("level"); -#undef kini - } - for (i = 0; i < 6; i++) { + 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; } - - /* - * no "proc" -> use "lambda" - */ - if (!args[2]) { Tcl_DictObjGet(NULL, info, *k, &val); args[2] = val ? TclGetString(val) : NULL; } k++; - - /* - * no "class" -> use "object" - */ - - if (!args[5]) { - Tcl_DictObjGet(NULL, info, *k, &val); - args[5] = val ? TclGetString(val) : NULL; - } - k++; for (i = 0; i < 2; i++) { Tcl_DictObjGet(NULL, info, *k++, &val); if (val) { - TclGetIntFromObj(NULL, val, &argsi[i]); + TclGetIntFromObj(NULL, val, &(argsi[i])); } else { argsi[i] = 0; } } } - -/* - *---------------------------------------------------------------------- - * - * DTraceCmdReturn -- - * - * NR callback for DTrace command return probes. - * - * Results: - * None. - * - * Side effects: - * None. - * - *---------------------------------------------------------------------- - */ - -static int -DTraceCmdReturn( - void *data[], - Tcl_Interp *interp, - int result) -{ - char *cmdName = TclGetString((Tcl_Obj *) data[0]); - - if (TCL_DTRACE_CMD_RETURN_ENABLED()) { - TCL_DTRACE_CMD_RETURN(cmdName, result); - } - if (TCL_DTRACE_CMD_RESULT_ENABLED()) { - Tcl_Obj *r = Tcl_GetObjResult(interp); - - TCL_DTRACE_CMD_RESULT(cmdName, result, TclGetString(r), r); - } - return result; -} TCL_DTRACE_DEBUG_LOG() #endif /* USE_DTRACE */ /* - *---------------------------------------------------------------------- - * - * Tcl_NRCallObjProc -- - * - * This function calls an objProc directly while managing things properly - * if it happens to be an NR objProc. It is meant to be used by extenders - * that provide an NR implementation of a command, as this function - * permits a trivial coding of the non-NR objProc. - * - * 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 objProc. - * - *---------------------------------------------------------------------- - */ - -int -Tcl_NRCallObjProc( - Tcl_Interp *interp, - Tcl_ObjCmdProc *objProc, - void *clientData, - Tcl_Size objc, - Tcl_Obj *const objv[]) -{ - NRE_callback *rootPtr = TOP_CB(interp); - - TclNRAddCallback(interp, Dispatch, objProc, clientData, - INT2PTR(objc), objv); - return TclNRRunCallbacks(interp, TCL_OK, rootPtr); -} - -/* - *---------------------------------------------------------------------- - * - * Tcl_NRCreateCommand -- - * - * Define a new NRE-enabled 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. - * - * 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. - * - * 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. - * - *---------------------------------------------------------------------- - */ - -Tcl_Command -Tcl_NRCreateCommand( - 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 function to associate with - * name, provides direct access for direct - * calls. */ - Tcl_ObjCmdProc *nreProc, /* Object-based function to associate with - * name, provides NR implementation */ - void *clientData, /* Arbitrary value to pass to object - * function. */ - Tcl_CmdDeleteProc *deleteProc) - /* If not NULL, gives a function to call when - * this command is deleted. */ -{ - Command *cmdPtr = (Command *) - Tcl_CreateObjCommand(interp, cmdName, proc, clientData, - deleteProc); - - cmdPtr->nreProc = nreProc; - return (Tcl_Command) cmdPtr; -} - -Tcl_Command -TclNRCreateCommandInNs( - Tcl_Interp *interp, - const char *cmdName, - Tcl_Namespace *nsPtr, - Tcl_ObjCmdProc *proc, - Tcl_ObjCmdProc *nreProc, - void *clientData, - Tcl_CmdDeleteProc *deleteProc) -{ - Command *cmdPtr = (Command *) - TclCreateObjCommandInNs(interp, cmdName, nsPtr, proc, clientData, - deleteProc); - - cmdPtr->nreProc = nreProc; - return (Tcl_Command) cmdPtr; -} - -/**************************************************************************** - * Stuff for the public api - ****************************************************************************/ - -int -Tcl_NREvalObj( - Tcl_Interp *interp, - Tcl_Obj *objPtr, - int flags) -{ - return TclNREvalObjEx(interp, objPtr, flags, NULL, INT_MIN); -} - -int -Tcl_NREvalObjv( - Tcl_Interp *interp, /* Interpreter in which to evaluate the - * command. Also used for error reporting. */ - Tcl_Size 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, TCL_EVAL_INVOKE and - * TCL_EVAL_NOERR are currently supported. */ -{ - return TclNREvalObjv(interp, objc, objv, flags, NULL); -} - -int -Tcl_NRCmdSwap( - Tcl_Interp *interp, - Tcl_Command cmd, - Tcl_Size objc, - Tcl_Obj *const objv[], - int flags) -{ - return TclNREvalObjv(interp, objc, objv, flags|TCL_EVAL_NOERR, - (Command *) cmd); -} - -/***************************************************************************** - * Tailcall related code - ***************************************************************************** - * - * The steps of the tailcall dance are as follows: - * - * 1. when [tailcall] is invoked, it stores the corresponding callback in - * the current CallFrame and returns TCL_RETURN - * 2. when the CallFrame is popped, it calls TclSetTailcall to store the - * callback in the proper NRCommand callback - the spot where the command - * that pushed the CallFrame is completely cleaned up - * 3. when the NRCommand callback runs, it schedules the tailcall callback - * to run immediately after it returns - * - * One delicate point is to properly define the NRCommand where the tailcall - * will execute. There are functions whose purpose is to help define the - * precise spot: - * TclMarkTailcall: if the NEXT command to be pushed tailcalls, execution - * should continue right here - * TclSkipTailcall: if the NEXT command to be pushed tailcalls, execution - * should continue after the CURRENT command is fully returned ("skip - * the next command: we are redirecting to it, tailcalls should run - * after WE return") - * TclPushTailcallPoint: the search for a tailcalling spot cannot traverse - * this point. This is special for OO, as some of the oo constructs - * that behave like commands may not push an NRCommand callback. - */ - -void -TclMarkTailcall( - Tcl_Interp *interp) -{ - Interp *iPtr = (Interp *) interp; - - if (iPtr->deferredCallbacks == NULL) { - TclNRAddCallback(interp, NRCommand, NULL, NULL, - NULL, NULL); - iPtr->deferredCallbacks = TOP_CB(interp); - } -} - -void -TclSkipTailcall( - Tcl_Interp *interp) -{ - Interp *iPtr = (Interp *) interp; - - TclMarkTailcall(interp); - iPtr->deferredCallbacks->data[1] = INT2PTR(1); -} - -void -TclPushTailcallPoint( - Tcl_Interp *interp) -{ - TclNRAddCallback(interp, NRCommand, NULL, NULL, NULL, NULL); - ((Interp *) interp)->numLevels++; -} - -/* - *---------------------------------------------------------------------- - * - * TclSetTailcall -- - * - * Splice a tailcall command in the proper spot of the NRE callback - * stack, so that it runs at the right time. - * - *---------------------------------------------------------------------- - */ - -void -TclSetTailcall( - Tcl_Interp *interp, - Tcl_Obj *listPtr) -{ - /* - * Find the splicing spot: right before the NRCommand of the thing - * being tailcalled. Note that we skip NRCommands marked by a 1 in data[1] - * (used by command redirectors). - */ - - NRE_callback *runPtr; - - for (runPtr = TOP_CB(interp); runPtr; runPtr = runPtr->nextPtr) { - if (((runPtr->procPtr) == NRCommand) && !runPtr->data[1]) { - break; - } - } - if (!runPtr) { - Tcl_Panic("tailcall cannot find the right splicing spot: should not happen!"); - } - runPtr->data[1] = listPtr; -} - -/* - *---------------------------------------------------------------------- - * - * TclNRTailcallObjCmd -- - * - * Prepare the tailcall as a list and store it in the current - * varFrame. When the frame is later popped the tailcall will be spliced - * at the proper place. - * - * Results: - * The first NRCommand callback that is not marked to be skipped is - * updated so that its data[1] field contains the tailcall list. - * - *---------------------------------------------------------------------- - */ - -int -TclNRTailcallObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Interp *iPtr = (Interp *) interp; - - if (objc < 1) { - Tcl_WrongNumArgs(interp, 1, objv, "?command? ?arg ...?"); - return TCL_ERROR; - } - - if (!(iPtr->varFramePtr->isProcCallFrame & 1)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "tailcall can only be called from a proc, lambda or method", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "TAILCALL", "ILLEGAL", (void *)NULL); - return TCL_ERROR; - } - - /* - * Invocation without args just clears a scheduled tailcall; invocation - * with an argument replaces any previously scheduled tailcall. - */ - - if (iPtr->varFramePtr->tailcallPtr) { - Tcl_DecrRefCount(iPtr->varFramePtr->tailcallPtr); - iPtr->varFramePtr->tailcallPtr = NULL; - } - - /* - * Create the callback to actually evaluate the tailcalled - * command, then set it in the varFrame so that PopCallFrame can use it - * at the proper time. - */ - - if (objc > 1) { - Tcl_Obj *listPtr, *nsObjPtr; - Tcl_Namespace *nsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr; - - /* - * The tailcall data is in a Tcl list: the first element is the - * namespace, the rest the command to be tailcalled. - */ - - nsObjPtr = Tcl_NewStringObj(nsPtr->fullName, TCL_INDEX_NONE); - listPtr = Tcl_NewListObj(objc, objv); - TclListObjSetElement(interp, listPtr, 0, nsObjPtr); - - iPtr->varFramePtr->tailcallPtr = listPtr; - } - return TCL_RETURN; -} - -/* - *---------------------------------------------------------------------- - * - * TclNRTailcallEval -- - * - * This NREcallback actually causes the tailcall to be evaluated. - * - *---------------------------------------------------------------------- - */ - -int -TclNRTailcallEval( - void *data[], - Tcl_Interp *interp, - int result) -{ - Interp *iPtr = (Interp *) interp; - Tcl_Obj *listPtr = (Tcl_Obj *)data[0], *nsObjPtr; - Tcl_Namespace *nsPtr; - Tcl_Size objc; - Tcl_Obj **objv; - - TclListObjGetElements(interp, listPtr, &objc, &objv); - nsObjPtr = objv[0]; - - if (result == TCL_OK) { - result = TclGetNamespaceFromObj(interp, nsObjPtr, &nsPtr); - } - - if (result != TCL_OK) { - /* - * Tailcall execution was preempted, eg by an intervening catch or by - * a now-gone namespace: cleanup and return. - */ - - Tcl_DecrRefCount(listPtr); - return result; - } - - /* - * Perform the tailcall - */ - - TclMarkTailcall(interp); - TclNRAddCallback(interp, TclNRReleaseValues, listPtr, NULL, NULL,NULL); - iPtr->lookupNsPtr = (Namespace *) nsPtr; - return TclNREvalObjv(interp, objc-1, objv+1, 0, NULL); -} - -int -TclNRReleaseValues( - void *data[], - TCL_UNUSED(Tcl_Interp *), - int result) -{ - int i = 0; - - while (i < 4) { - if (data[i]) { - Tcl_DecrRefCount((Tcl_Obj *) data[i]); - } else { - break; - } - i++; - } - return result; -} - -void -Tcl_NRAddCallback( - Tcl_Interp *interp, - Tcl_NRPostProc *postProcPtr, - void *data0, - void *data1, - void *data2, - void *data3) -{ - if (!(postProcPtr)) { - Tcl_Panic("Adding a callback without an objProc?!"); - } - TclNRAddCallback(interp, postProcPtr, data0, data1, data2, data3); -} - -/* - *---------------------------------------------------------------------- - * - * TclNRCoroutineObjCmd -- (and friends) - * - * This object-based function is invoked to process the "coroutine" Tcl - * command. It is heavily based on "apply". - * - * Results: - * A standard Tcl object result value. - * - * Side effects: - * A new procedure gets created. - * - * ** FIRST EXPERIMENTAL IMPLEMENTATION ** - * - * It is fairly amateurish and not up to our standards - mainly in terms of - * error messages and [info] interaction. Just to test the infrastructure in - * teov and tebc. - *---------------------------------------------------------------------- - */ - -#define iPtr ((Interp *) interp) - -int -TclNRYieldObjCmd( - void *clientData, - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - CoroutineData *corPtr = iPtr->execEnvPtr->corPtr; - - if (objc > 2) { - Tcl_WrongNumArgs(interp, 1, objv, "?returnValue?"); - return TCL_ERROR; - } - - if (!corPtr) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "yield can only be called in a coroutine", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ILLEGAL_YIELD", (void *)NULL); - return TCL_ERROR; - } - - if (objc == 2) { - Tcl_SetObjResult(interp, objv[1]); - } - - NRE_ASSERT(!COR_IS_SUSPENDED(corPtr)); - TclNRAddCallback(interp, TclNRCoroutineActivateCallback, corPtr, - clientData, NULL, NULL); - return TCL_OK; -} - -int -TclNRYieldToObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - CoroutineData *corPtr = iPtr->execEnvPtr->corPtr; - Tcl_Obj *listPtr, *nsObjPtr; - Tcl_Namespace *nsPtr = TclGetCurrentNamespace(interp); - - if (objc < 2) { - Tcl_WrongNumArgs(interp, 1, objv, "command ?arg ...?"); - return TCL_ERROR; - } - - if (!corPtr) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "yieldto can only be called in a coroutine", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ILLEGAL_YIELD", (void *)NULL); - return TCL_ERROR; - } - - if (((Namespace *) nsPtr)->flags & NS_DYING) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "yieldto called in deleted namespace", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "YIELDTO_IN_DELETED", - (void *)NULL); - return TCL_ERROR; - } - - /* - * Add the tailcall in the caller env, then just yield. - * - * This is essentially code from TclNRTailcallObjCmd - */ - - listPtr = Tcl_NewListObj(objc, objv); - nsObjPtr = Tcl_NewStringObj(nsPtr->fullName, TCL_INDEX_NONE); - TclListObjSetElement(interp, listPtr, 0, nsObjPtr); - - /* - * Add the callback in the caller's env, then instruct TEBC to yield. - */ - - iPtr->execEnvPtr = corPtr->callerEEPtr; - /* Not calling Tcl_IncrRefCount(listPtr) here because listPtr is private */ - TclSetTailcall(interp, listPtr); - corPtr->yieldPtr = listPtr; - iPtr->execEnvPtr = corPtr->eePtr; - - return TclNRYieldObjCmd(CORO_ACTIVATE_YIELDM, interp, 1, objv); -} - -static int -RewindCoroutineCallback( - void *data[], - Tcl_Interp *interp, - TCL_UNUSED(int) /*result*/) -{ - return Tcl_RestoreInterpState(interp, (Tcl_InterpState)data[0]); -} - -static int -RewindCoroutine( - CoroutineData *corPtr, - int result) -{ - Tcl_Interp *interp = corPtr->eePtr->interp; - Tcl_InterpState state = Tcl_SaveInterpState(interp, result); - - NRE_ASSERT(COR_IS_SUSPENDED(corPtr)); - NRE_ASSERT(corPtr->eePtr != NULL); - NRE_ASSERT(corPtr->eePtr != iPtr->execEnvPtr); - - corPtr->eePtr->rewind = 1; - TclNRAddCallback(interp, RewindCoroutineCallback, state, - NULL, NULL, NULL); - return TclNRInterpCoroutine(corPtr, interp, 0, NULL); -} - -static void -DeleteCoroutine( - void *clientData) -{ - CoroutineData *corPtr = (CoroutineData *)clientData; - Tcl_Interp *interp = corPtr->eePtr->interp; - NRE_callback *rootPtr = TOP_CB(interp); - - if (COR_IS_SUSPENDED(corPtr)) { - TclNRRunCallbacks(interp, RewindCoroutine(corPtr,TCL_OK), rootPtr); - } -} - -static int -NRCoroutineCallerCallback( - void *data[], - Tcl_Interp *interp, - int result) -{ - CoroutineData *corPtr = (CoroutineData *)data[0]; - Command *cmdPtr = corPtr->cmdPtr; - - /* - * This is the last callback in the caller execEnv, right before switching - * to the coroutine's - */ - - NRE_ASSERT(iPtr->execEnvPtr == corPtr->callerEEPtr); - - if (!corPtr->eePtr) { - /* - * The execEnv was wound down but not deleted for our sake. We finish - * the job here. The caller context has already been restored. - */ - - NRE_ASSERT(iPtr->varFramePtr == corPtr->caller.varFramePtr); - NRE_ASSERT(iPtr->framePtr == corPtr->caller.framePtr); - NRE_ASSERT(iPtr->cmdFramePtr == corPtr->caller.cmdFramePtr); - ckfree(corPtr); - return result; - } - - NRE_ASSERT(COR_IS_SUSPENDED(corPtr)); - SAVE_CONTEXT(corPtr->running); - RESTORE_CONTEXT(corPtr->caller); - - if (cmdPtr->flags & CMD_DYING) { - /* - * The command was deleted while it was running: wind down the - * execEnv, this will do the complete cleanup. RewindCoroutine will - * restore both the caller's context and interp state. - */ - - return RewindCoroutine(corPtr, result); - } - - return result; -} - -static int -NRCoroutineExitCallback( - void *data[], - Tcl_Interp *interp, - int result) -{ - CoroutineData *corPtr = (CoroutineData *)data[0]; - Command *cmdPtr = corPtr->cmdPtr; - - /* - * This runs at the bottom of the Coroutine's execEnv: it will be executed - * when the coroutine returns or is wound down, but not when it yields. It - * deletes the coroutine and restores the caller's environment. - */ - - NRE_ASSERT(interp == corPtr->eePtr->interp); - NRE_ASSERT(TOP_CB(interp) == NULL); - NRE_ASSERT(iPtr->execEnvPtr == corPtr->eePtr); - NRE_ASSERT(!COR_IS_SUSPENDED(corPtr)); - NRE_ASSERT((corPtr->callerEEPtr->callbackPtr->procPtr == NRCoroutineCallerCallback)); - - cmdPtr->deleteProc = NULL; - Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); - TclCleanupCommandMacro(cmdPtr); - - corPtr->eePtr->corPtr = NULL; - TclDeleteExecEnv(corPtr->eePtr); - corPtr->eePtr = NULL; - - corPtr->stackLevel = NULL; - - /* - * #280. - * Drop the coroutine-owned copy of the lineLABCPtr hashtable for literal - * command arguments in bytecode. - */ - - Tcl_DeleteHashTable(corPtr->lineLABCPtr); - ckfree(corPtr->lineLABCPtr); - corPtr->lineLABCPtr = NULL; - - RESTORE_CONTEXT(corPtr->caller); - iPtr->execEnvPtr = corPtr->callerEEPtr; - iPtr->numLevels++; - - return result; -} - -/* - *---------------------------------------------------------------------- - * - * TclNRCoroutineActivateCallback -- - * - * This is the workhorse for coroutines: it implements both yield and - * resume. - * - * It is important that both be implemented in the same callback: the - * detection of the impossibility to suspend due to a busy C-stack relies - * on the precise position of a local variable in the stack. We do not - * want the compiler to play tricks on us, either by moving things around - * or inlining. - * - *---------------------------------------------------------------------- - */ - -int -TclNRCoroutineActivateCallback( - void *data[], - Tcl_Interp *interp, - TCL_UNUSED(int) /*result*/) -{ - CoroutineData *corPtr = (CoroutineData *)data[0]; - void *stackLevel = TclGetCStackPtr(); - - if (!corPtr->stackLevel) { - /* - * -- Coroutine is suspended -- - * Push the callback to restore the caller's context on yield or - * return. - */ - - TclNRAddCallback(interp, NRCoroutineCallerCallback, corPtr, - NULL, NULL, NULL); - - /* - * Record the stackLevel at which the resume is happening, then swap - * the interp's environment to make it suitable to run this coroutine. - */ - - corPtr->stackLevel = stackLevel; - Tcl_Size numLevels = corPtr->auxNumLevels; - corPtr->auxNumLevels = iPtr->numLevels; - - SAVE_CONTEXT(corPtr->caller); - corPtr->callerEEPtr = iPtr->execEnvPtr; - RESTORE_CONTEXT(corPtr->running); - iPtr->execEnvPtr = corPtr->eePtr; - iPtr->numLevels += numLevels; - } else { - /* - * Coroutine is active: yield - */ - - if (corPtr->stackLevel != stackLevel) { - NRE_callback *runPtr; - - iPtr->execEnvPtr = corPtr->callerEEPtr; - if (corPtr->yieldPtr) { - for (runPtr = TOP_CB(interp); runPtr; runPtr = runPtr->nextPtr) { - if (runPtr->data[1] == corPtr->yieldPtr) { - Tcl_DecrRefCount((Tcl_Obj *)runPtr->data[1]); - runPtr->data[1] = NULL; - corPtr->yieldPtr = NULL; - break; - } - } - } - iPtr->execEnvPtr = corPtr->eePtr; - - - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "cannot yield: C stack busy", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "CANT_YIELD", - (void *)NULL); - return TCL_ERROR; - } - - void *type = data[1]; - if (type == CORO_ACTIVATE_YIELD) { - corPtr->nargs = COROUTINE_ARGUMENTS_SINGLE_OPTIONAL; - } else if (type == CORO_ACTIVATE_YIELDM) { - corPtr->nargs = COROUTINE_ARGUMENTS_ARBITRARY; - } else { - Tcl_Panic("Yield received an option which is not implemented"); - } - - corPtr->yieldPtr = NULL; - corPtr->stackLevel = NULL; - - Tcl_Size numLevels = iPtr->numLevels; - iPtr->numLevels = corPtr->auxNumLevels; - corPtr->auxNumLevels = numLevels - corPtr->auxNumLevels; - - iPtr->execEnvPtr = corPtr->callerEEPtr; - } - - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclNREvalList -- - * - * Callback to invoke command as list, used in order to delayed - * processing of canonical list command in sane environment. - * - *---------------------------------------------------------------------- - */ - -static int -TclNREvalList( - void *data[], - Tcl_Interp *interp, - TCL_UNUSED(int) /*result*/) -{ - Tcl_Size objc; - Tcl_Obj **objv; - Tcl_Obj *listPtr = (Tcl_Obj *)data[0]; - - Tcl_IncrRefCount(listPtr); - - TclMarkTailcall(interp); - TclNRAddCallback(interp, TclNRReleaseValues, listPtr, NULL, NULL,NULL); - TclListObjGetElements(NULL, listPtr, &objc, &objv); - return TclNREvalObjv(interp, objc, objv, 0, NULL); -} - -/* - *---------------------------------------------------------------------- - * - * CoroTypeObjCmd -- - * - * Implementation of [::tcl::unsupported::corotype] command. - * - *---------------------------------------------------------------------- - */ - -static int -CoroTypeObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - Command *cmdPtr; - CoroutineData *corPtr; - - if (objc != 2) { - Tcl_WrongNumArgs(interp, 1, objv, "coroName"); - return TCL_ERROR; - } - - /* - * Look up the coroutine. - */ - - cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[1]); - if ((!cmdPtr) || (cmdPtr->nreProc != TclNRInterpCoroutine)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can only get coroutine type of a coroutine", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COROUTINE", - TclGetString(objv[1]), (void *)NULL); - return TCL_ERROR; - } - - /* - * An active coroutine is "active". Can't tell what it might do in the - * future. - */ - - corPtr = (CoroutineData *)cmdPtr->objClientData; - if (!COR_IS_SUSPENDED(corPtr)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj("active", TCL_INDEX_NONE)); - return TCL_OK; - } - - /* - * Inactive coroutines are classified by the (effective) command used to - * suspend them, which matters when you're injecting a probe. - */ - - switch (corPtr->nargs) { - case COROUTINE_ARGUMENTS_SINGLE_OPTIONAL: - Tcl_SetObjResult(interp, Tcl_NewStringObj("yield", TCL_INDEX_NONE)); - return TCL_OK; - case COROUTINE_ARGUMENTS_ARBITRARY: - Tcl_SetObjResult(interp, Tcl_NewStringObj("yieldto", TCL_INDEX_NONE)); - return TCL_OK; - default: - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "unknown coroutine type", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "BAD_TYPE", (void *)NULL); - return TCL_ERROR; - } -} - -/* - *---------------------------------------------------------------------- - * - * TclNRCoroInjectObjCmd, TclNRCoroProbeObjCmd -- - * - * Implementation of [coroinject] and [coroprobe] commands. - * - *---------------------------------------------------------------------- - */ - -static inline CoroutineData * -GetCoroutineFromObj( - Tcl_Interp *interp, - Tcl_Obj *objPtr, - const char *errMsg) -{ - /* - * How to get a coroutine from its handle. - */ - - Command *cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objPtr); - - if ((!cmdPtr) || (cmdPtr->nreProc != TclNRInterpCoroutine)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj(errMsg, TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COROUTINE", - TclGetString(objPtr), (void *)NULL); - return NULL; - } - return (CoroutineData *)cmdPtr->objClientData; -} - -static int -TclNRCoroInjectObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - CoroutineData *corPtr; - - /* - * Usage more or less like tailcall: - * coroinject coroName cmd ?arg1 arg2 ...? - */ - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "coroName cmd ?arg1 arg2 ...?"); - return TCL_ERROR; - } - - corPtr = GetCoroutineFromObj(interp, objv[1], - "can only inject a command into a coroutine"); - if (!corPtr) { - return TCL_ERROR; - } - if (!COR_IS_SUSPENDED(corPtr)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can only inject a command into a suspended coroutine", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ACTIVE", (void *)NULL); - return TCL_ERROR; - } - - /* - * Add the callback to the coro's execEnv, so that it is the first thing - * to happen when the coro is resumed. - */ - - ExecEnv *savedEEPtr = iPtr->execEnvPtr; - iPtr->execEnvPtr = corPtr->eePtr; - TclNRAddCallback(interp, InjectHandler, corPtr, - Tcl_NewListObj(objc - 2, objv + 2), INT2PTR(corPtr->nargs), NULL); - iPtr->execEnvPtr = savedEEPtr; - - return TCL_OK; -} - -static int -TclNRCoroProbeObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - CoroutineData *corPtr; - - /* - * Usage more or less like tailcall: - * coroprobe coroName cmd ?arg1 arg2 ...? - */ - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "coroName cmd ?arg1 arg2 ...?"); - return TCL_ERROR; - } - - corPtr = GetCoroutineFromObj(interp, objv[1], - "can only inject a probe command into a coroutine"); - if (!corPtr) { - return TCL_ERROR; - } - if (!COR_IS_SUSPENDED(corPtr)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can only inject a probe command into a suspended coroutine", - TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ACTIVE", (void *)NULL); - return TCL_ERROR; - } - - /* - * Add the callback to the coro's execEnv, so that it is the first thing - * to happen when the coro is resumed. - */ - - ExecEnv *savedEEPtr = iPtr->execEnvPtr; - iPtr->execEnvPtr = corPtr->eePtr; - TclNRAddCallback(interp, InjectHandler, corPtr, - Tcl_NewListObj(objc - 2, objv + 2), INT2PTR(corPtr->nargs), corPtr); - iPtr->execEnvPtr = savedEEPtr; - - /* - * Now we immediately transfer control to the coroutine to run our probe. - * TRICKY STUFF copied from the [yield] implementation. - * - * Push the callback to restore the caller's context on yield back. - */ - - TclNRAddCallback(interp, NRCoroutineCallerCallback, corPtr, - NULL, NULL, NULL); - - /* - * Record the stackLevel at which the resume is happening, then swap - * the interp's environment to make it suitable to run this coroutine. - */ - - corPtr->stackLevel = &corPtr; - Tcl_Size numLevels = corPtr->auxNumLevels; - corPtr->auxNumLevels = iPtr->numLevels; - - /* - * Do the actual stack swap. - */ - - SAVE_CONTEXT(corPtr->caller); - corPtr->callerEEPtr = iPtr->execEnvPtr; - RESTORE_CONTEXT(corPtr->running); - iPtr->execEnvPtr = corPtr->eePtr; - iPtr->numLevels += numLevels; - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * InjectHandler, InjectHandlerPostProc -- - * - * Part of the implementation of [coroinject] and [coroprobe]. These are - * run inside the context of the coroutine being injected/probed into. - * - * InjectHandler runs a script (possibly adding arguments) in the context - * of the coroutine. The script is specified as a one-shot list (with - * reference count equal to 1) in data[1]. This function also arranges - * for InjectHandlerPostProc to be the part that runs after the script - * completes. - * - * InjectHandlerPostProc cleans up after InjectHandler (deleting the - * list) and, for the [coroprobe] command *only*, yields back to the - * caller context (i.e., where [coroprobe] was run). - *s - *---------------------------------------------------------------------- - */ - -static int -InjectHandler( - void *data[], - Tcl_Interp *interp, - TCL_UNUSED(int) /*result*/) -{ - CoroutineData *corPtr = (CoroutineData *)data[0]; - Tcl_Obj *listPtr = (Tcl_Obj *)data[1]; - Tcl_Size nargs = PTR2INT(data[2]); - void *isProbe = data[3]; - Tcl_Size objc; - Tcl_Obj **objv; - - if (!isProbe) { - /* - * If this is [coroinject], add the extra arguments now. - */ - - if (nargs == COROUTINE_ARGUMENTS_SINGLE_OPTIONAL) { - Tcl_ListObjAppendElement(NULL, listPtr, - Tcl_NewStringObj("yield", TCL_INDEX_NONE)); - } else if (nargs == COROUTINE_ARGUMENTS_ARBITRARY) { - Tcl_ListObjAppendElement(NULL, listPtr, - Tcl_NewStringObj("yieldto", TCL_INDEX_NONE)); - } else { - /* - * I don't think this is reachable... - */ - Tcl_Obj *nargsObj; - TclNewIndexObj(nargsObj, nargs); - Tcl_ListObjAppendElement(NULL, listPtr, nargsObj); - } - Tcl_ListObjAppendElement(NULL, listPtr, Tcl_GetObjResult(interp)); - } - - /* - * Call the user's script; we're in the right place. - */ - - Tcl_IncrRefCount(listPtr); - TclMarkTailcall(interp); - TclNRAddCallback(interp, InjectHandlerPostCall, corPtr, listPtr, - INT2PTR(nargs), isProbe); - TclListObjGetElements(NULL, listPtr, &objc, &objv); - return TclNREvalObjv(interp, objc, objv, 0, NULL); -} - -static int -InjectHandlerPostCall( - void *data[], - Tcl_Interp *interp, - int result) -{ - CoroutineData *corPtr = (CoroutineData *)data[0]; - Tcl_Obj *listPtr = (Tcl_Obj *)data[1]; - Tcl_Size nargs = PTR2INT(data[2]); - void *isProbe = data[3]; - - /* - * Delete the command words for what we just executed. - */ - - Tcl_DecrRefCount(listPtr); - - /* - * If we were doing a probe, splice ourselves back out of the stack - * cleanly here. General injection should instead just look after itself. - * - * Code from guts of [yield] implementation. - */ - - if (isProbe) { - if (result == TCL_ERROR) { - Tcl_AddErrorInfo(interp, - "\n (injected coroutine probe command)"); - } - corPtr->nargs = nargs; - corPtr->stackLevel = NULL; - Tcl_Size numLevels = iPtr->numLevels; - iPtr->numLevels = corPtr->auxNumLevels; - corPtr->auxNumLevels = numLevels - corPtr->auxNumLevels; - iPtr->execEnvPtr = corPtr->callerEEPtr; - } - return result; -} - -/* - *---------------------------------------------------------------------- - * - * NRInjectObjCmd -- - * - * Implementation of [::tcl::unsupported::inject] command. - * - *---------------------------------------------------------------------- - */ - -static int -NRInjectObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - CoroutineData *corPtr; - ExecEnv *savedEEPtr = iPtr->execEnvPtr; - - /* - * Usage more or less like tailcall: - * inject coroName cmd ?arg1 arg2 ...? - */ - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "coroName cmd ?arg1 arg2 ...?"); - return TCL_ERROR; - } - - corPtr = GetCoroutineFromObj(interp, objv[1], - "can only inject a command into a coroutine"); - if (!corPtr) { - return TCL_ERROR; - } - if (!COR_IS_SUSPENDED(corPtr)) { - Tcl_SetObjResult(interp, Tcl_NewStringObj( - "can only inject a command into a suspended coroutine", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ACTIVE", (void *)NULL); - return TCL_ERROR; - } - - /* - * Add the callback to the coro's execEnv, so that it is the first thing - * to happen when the coro is resumed. - */ - - iPtr->execEnvPtr = corPtr->eePtr; - TclNRAddCallback(interp, TclNREvalList, Tcl_NewListObj(objc-2, objv+2), - NULL, NULL, NULL); - iPtr->execEnvPtr = savedEEPtr; - - return TCL_OK; -} - -int -TclNRInterpCoroutine( - void *clientData, - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - CoroutineData *corPtr = (CoroutineData *)clientData; - - if (!COR_IS_SUSPENDED(corPtr)) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "coroutine \"%s\" is already running", - TclGetString(objv[0]))); - Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "BUSY", (void *)NULL); - return TCL_ERROR; - } - - /* - * Parse all the arguments to work out what to feed as the result of the - * [yield]. TRICKY POINT: objc==0 happens here! It occurs when a coroutine - * is deleted! - */ - - switch (corPtr->nargs) { - case COROUTINE_ARGUMENTS_SINGLE_OPTIONAL: - if (objc == 2) { - Tcl_SetObjResult(interp, objv[1]); - } else if (objc > 2) { - Tcl_WrongNumArgs(interp, 1, objv, "?arg?"); - return TCL_ERROR; - } - break; - default: - if (corPtr->nargs + 1 != objc) { - Tcl_SetObjResult(interp, - Tcl_NewStringObj("wrong coro nargs; how did we get here? " - "not implemented!", TCL_INDEX_NONE)); - Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", (void *)NULL); - return TCL_ERROR; - } - /* fallthrough */ - case COROUTINE_ARGUMENTS_ARBITRARY: - if (objc > 1) { - Tcl_SetObjResult(interp, Tcl_NewListObj(objc-1, objv+1)); - } - break; - } - - TclNRAddCallback(interp, TclNRCoroutineActivateCallback, corPtr, - NULL, NULL, NULL); - return TCL_OK; -} - -/* - *---------------------------------------------------------------------- - * - * TclNRCoroutineObjCmd -- - * - * Implementation of [coroutine] command; see documentation for - * description of what this does. - * - *---------------------------------------------------------------------- - */ - -int -TclNRCoroutineObjCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, /* Current interpreter. */ - int objc, /* Number of arguments. */ - Tcl_Obj *const objv[]) /* Argument objects. */ -{ - Command *cmdPtr; - CoroutineData *corPtr; - const char *procName, *simpleName; - Namespace *nsPtr, *altNsPtr, *cxtNsPtr, - *inNsPtr = (Namespace *)TclGetCurrentNamespace(interp); - Namespace *lookupNsPtr = iPtr->varFramePtr->nsPtr; - - if (objc < 3) { - Tcl_WrongNumArgs(interp, 1, objv, "name cmd ?arg ...?"); - return TCL_ERROR; - } - - procName = TclGetString(objv[1]); - TclGetNamespaceForQualName(interp, procName, inNsPtr, 0, - &nsPtr, &altNsPtr, &cxtNsPtr, &simpleName); - - if (nsPtr == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't create procedure \"%s\": unknown namespace", - procName)); - Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", (void *)NULL); - return TCL_ERROR; - } - if (simpleName == NULL) { - Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "can't create procedure \"%s\": bad procedure name", - procName)); - Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", procName, (void *)NULL); - return TCL_ERROR; - } - - /* - * We ARE creating the coroutine command: allocate the corresponding - * struct and create the corresponding command. - */ - - corPtr = (CoroutineData *)ckalloc(sizeof(CoroutineData)); - - cmdPtr = (Command *) TclNRCreateCommandInNs(interp, simpleName, - (Tcl_Namespace *)nsPtr, /*objProc*/ NULL, TclNRInterpCoroutine, - corPtr, DeleteCoroutine); - - corPtr->cmdPtr = cmdPtr; - cmdPtr->refCount++; - - /* - * #280. - * Provide the new coroutine with its own copy of the lineLABCPtr - * hashtable for literal command arguments in bytecode. Note that that - * CFWordBC chains are not duplicated, only the entrypoints to them. This - * means that in the presence of coroutines each chain is potentially a - * tree. Like the chain -> tree conversion of the CmdFrame stack. - */ - - { - Tcl_HashSearch hSearch; - Tcl_HashEntry *hePtr; - - corPtr->lineLABCPtr = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); - Tcl_InitHashTable(corPtr->lineLABCPtr, TCL_ONE_WORD_KEYS); - - for (hePtr = Tcl_FirstHashEntry(iPtr->lineLABCPtr,&hSearch); - hePtr; hePtr = Tcl_NextHashEntry(&hSearch)) { - int isNew; - Tcl_HashEntry *newPtr = - Tcl_CreateHashEntry(corPtr->lineLABCPtr, - Tcl_GetHashKey(iPtr->lineLABCPtr, hePtr), - &isNew); - - Tcl_SetHashValue(newPtr, Tcl_GetHashValue(hePtr)); - } - } - - /* - * Create the base context. - */ - - corPtr->running.framePtr = iPtr->rootFramePtr; - corPtr->running.varFramePtr = iPtr->rootFramePtr; - corPtr->running.cmdFramePtr = NULL; - corPtr->running.lineLABCPtr = corPtr->lineLABCPtr; - corPtr->stackLevel = NULL; - corPtr->auxNumLevels = 0; - corPtr->yieldPtr = NULL; - - /* - * Create the coro's execEnv, switch to it to push the exit and coro - * command callbacks, then switch back. - */ - - corPtr->eePtr = TclCreateExecEnv(interp, CORO_STACK_INITIAL_SIZE); - corPtr->callerEEPtr = iPtr->execEnvPtr; - corPtr->eePtr->corPtr = corPtr; - - SAVE_CONTEXT(corPtr->caller); - corPtr->callerEEPtr = iPtr->execEnvPtr; - RESTORE_CONTEXT(corPtr->running); - iPtr->execEnvPtr = corPtr->eePtr; - - TclNRAddCallback(interp, NRCoroutineExitCallback, corPtr, - NULL, NULL, NULL); - - /* - * Ensure that the command is looked up in the correct namespace. - */ - - iPtr->lookupNsPtr = lookupNsPtr; - Tcl_NREvalObj(interp, Tcl_NewListObj(objc - 2, objv + 2), 0); - iPtr->numLevels--; - - SAVE_CONTEXT(corPtr->running); - RESTORE_CONTEXT(corPtr->caller); - iPtr->execEnvPtr = corPtr->callerEEPtr; - - /* - * Now just resume the coroutine. - */ - - TclNRAddCallback(interp, TclNRCoroutineActivateCallback, corPtr, - NULL, NULL, NULL); - return TCL_OK; -} - -/* - * This is used in the [info] ensemble - */ - -int -TclInfoCoroutineCmd( - TCL_UNUSED(void *), - Tcl_Interp *interp, - int objc, - Tcl_Obj *const objv[]) -{ - CoroutineData *corPtr = iPtr->execEnvPtr->corPtr; - - if (objc != 1) { - Tcl_WrongNumArgs(interp, 1, objv, NULL); - return TCL_ERROR; - } - - if (corPtr && !(corPtr->cmdPtr->flags & CMD_DYING)) { - Tcl_Obj *namePtr; - - TclNewObj(namePtr); - Tcl_GetCommandFullName(interp, (Tcl_Command) corPtr->cmdPtr, namePtr); - Tcl_SetObjResult(interp, namePtr); - } - return TCL_OK; -} - -#undef iPtr - -/* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 - * tab-width: 8 - * indent-tabs-mode: nil * End: */ |
