diff options
Diffstat (limited to 'generic/tclBasic.c')
| -rw-r--r-- | generic/tclBasic.c | 9976 | 
1 files changed, 6240 insertions, 3736 deletions
| diff --git a/generic/tclBasic.c b/generic/tclBasic.c index dbdcd7c..2a334c4 100644 --- a/generic/tclBasic.c +++ b/generic/tclBasic.c @@ -1,281 +1,436 @@ -/*  +/*   * tclBasic.c --   *   *	Contains the basic facilities for TCL command interpretation, - *	including interpreter creation and deletion, command creation - *	and deletion, and command/script execution.  + *	including interpreter creation and deletion, command creation and + *	deletion, and command/script execution.   *   * Copyright (c) 1987-1994 The Regents of the University of California.   * Copyright (c) 1994-1997 Sun Microsystems, Inc.   * 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> + * Copyright (c) 2006-2008 by Joe Mistachkin.  All rights reserved. + * Copyright (c) 2008 Miguel Sofer <msofer@users.sourceforge.net>   * - * See the file "license.terms" for information on usage and redistribution - * of this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclBasic.c,v 1.75.2.34 2008/08/14 02:12:25 das Exp $ + * 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" -#ifndef TCL_GENERIC_ONLY -#   include "tclPort.h" +#include "tommath.h" +#include <math.h> + +#if NRE_ENABLE_ASSERTS +#include <assert.h>  #endif +#define INTERP_STACK_INITIAL_SIZE 2000 +#define CORO_STACK_INITIAL_SIZE    200 +  /* - * Static procedures in this file: + * Determine whether we're using IEEE floating point   */ -static char *		CallCommandTraces _ANSI_ARGS_((Interp *iPtr,  -			    Command *cmdPtr, CONST char *oldName,  -			    CONST char* newName, int flags)); -static void		DeleteInterpProc _ANSI_ARGS_((Tcl_Interp *interp)); -static void		ProcessUnexpectedResult _ANSI_ARGS_(( -			    Tcl_Interp *interp, int returnCode)); -static int	        StringTraceProc _ANSI_ARGS_((ClientData clientData, -						     Tcl_Interp* interp, -						     int level, -						     CONST char* command, -						    Tcl_Command commandInfo, -						    int objc, -						    Tcl_Obj *CONST objv[])); -static void           StringTraceDeleteProc _ANSI_ARGS_((ClientData clientData)); - -#ifdef TCL_TIP280 -/* TIP #280 - Modified token based evulation, with line information */ -static int            EvalEx _ANSI_ARGS_((Tcl_Interp *interp, CONST char *script, -					  int numBytes, int flags, int line)); - -static int            EvalTokensStandard _ANSI_ARGS_((Tcl_Interp *interp, -						      Tcl_Token *tokenPtr, -						      int count, int line)); - +#if (FLT_RADIX == 2) && (DBL_MANT_DIG == 53) && (DBL_MAX_EXP == 1024) +#   define IEEE_FLOATING_POINT +/* Largest odd integer that can be represented exactly in a double */ +#   define MAX_EXACT 9007199254740991.0  #endif +/* + * The following structure defines the client data for a math function + * registered with Tcl_CreateMathFunc + */ + +typedef struct OldMathFuncData { +    Tcl_MathProc *proc;		/* Handler function */ +    int numArgs;		/* Number of args expected */ +    Tcl_ValueType *argTypes;	/* Types of the args */ +    ClientData clientData;	/* Client data for the handler function */ +} OldMathFuncData; + +/* + * 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. */ +    int length;			/* Length of the above error message. */ +    ClientData clientData;	/* Ignored */ +    int flags;			/* Additional flags */ +} CancelInfo; +static Tcl_HashTable cancelTable; +static int cancelTableInitialized = 0;	/* 0 means not yet initialized. */ +TCL_DECLARE_MUTEX(cancelLock) + +/* + * 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 char *		CallCommandTraces(Interp *iPtr, Command *cmdPtr, +			    const char *oldName, const char *newName, +			    int flags); +static int		CancelEvalProc(ClientData clientData, +			    Tcl_Interp *interp, int code); +static int		CheckDoubleResult(Tcl_Interp *interp, double dResult); +static void		DeleteCoroutine(ClientData clientData); +static void		DeleteInterpProc(Tcl_Interp *interp); +static void		DeleteOpCmdClientData(ClientData clientData);  #ifdef USE_DTRACE -static int	DTraceObjCmd(ClientData dummy, Tcl_Interp *interp, int objc, -		    Tcl_Obj *CONST objv[]); -#endif +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	ExprEntierFunc; +static Tcl_ObjCmdProc	ExprFloorFunc; +static Tcl_ObjCmdProc	ExprIntFunc; +static Tcl_ObjCmdProc	ExprIsqrtFunc; +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 void		MathFuncWrongNumArgs(Tcl_Interp *interp, int expected, +			    int actual, Tcl_Obj *const *objv); +static Tcl_NRPostProc	NRCoroutineCallerCallback; +static Tcl_NRPostProc	NRCoroutineExitCallback; +static int NRCommand(ClientData data[], Tcl_Interp *interp, int result); + +static Tcl_ObjCmdProc	OldMathFuncProc; +static void		OldMathFuncDeleteProc(ClientData clientData); +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, +			    int 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, int objc, +			    Tcl_Obj *const objv[], Namespace *lookupNsPtr); +static int		TEOV_RunEnterTraces(Tcl_Interp *interp, +			    Command **cmdPtrPtr, Tcl_Obj *commandPtr, int objc, +			    Tcl_Obj *const objv[]); +static Tcl_NRPostProc	RewindCoroutineCallback; +static Tcl_NRPostProc	TailcallCleanup; +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 NRCoroInjectObjCmd; +static Tcl_NRPostProc NRPostInvoke; + +MODULE_SCOPE const TclStubs tclStubs; + +/* + * Magical counts for the number of arguments accepted by a coroutine command + * after particular kinds of [yield]. + */ -extern TclStubs tclStubs; +#define CORO_ACTIVATE_YIELD    PTR2INT(NULL) +#define CORO_ACTIVATE_YIELDM   PTR2INT(NULL)+1 +#define COROUTINE_ARGUMENTS_SINGLE_OPTIONAL     (-1) +#define COROUTINE_ARGUMENTS_ARBITRARY           (-2) +  /* - * The following structure defines the commands in the Tcl core. + * The following structure define the commands in the Tcl core.   */  typedef struct { -    char *name;			/* Name of object-based command. */ -    Tcl_CmdProc *proc;		/* String-based procedure for command. */ -    Tcl_ObjCmdProc *objProc;	/* Object-based procedure for command. */ -    CompileProc *compileProc;	/* Procedure called to compile command. */ -    int isSafe;			/* If non-zero, command will be present -                                 * in safe interpreter. Otherwise it will -                                 * be hidden. */ +    const char *name;		/* Name of object-based command. */ +    Tcl_ObjCmdProc *objProc;	/* Object-based function for command. */ +    CompileProc *compileProc;	/* Function called to compile command. */ +    Tcl_ObjCmdProc *nreProc;	/* NR-based function for command */ +    int flags;			/* Various flag bits, as defined below. */  } 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 built-in commands, and the procedures that implement them: + * The built-in commands, and the functions that implement them:   */ -static CmdInfo builtInCmds[] = { -    /* -     * Commands in the generic core. Note that at least one of the proc or -     * objProc members should be non-NULL. This avoids infinitely recursive -     * calls between TclInvokeObjectCommand and TclInvokeStringCommand if a -     * command name is computed at runtime and results in the name of a -     * compiled command. -     */ - -    {"append",		(Tcl_CmdProc *) NULL,	Tcl_AppendObjCmd, -	TclCompileAppendCmd,		1}, -    {"array",		(Tcl_CmdProc *) NULL,	Tcl_ArrayObjCmd, -        (CompileProc *) NULL,		1}, -    {"binary",		(Tcl_CmdProc *) NULL,	Tcl_BinaryObjCmd, -        (CompileProc *) NULL,		1}, -    {"break",		(Tcl_CmdProc *) NULL,	Tcl_BreakObjCmd, -        TclCompileBreakCmd,		1}, -    {"case",		(Tcl_CmdProc *) NULL,	Tcl_CaseObjCmd, -        (CompileProc *) NULL,		1}, -    {"catch",		(Tcl_CmdProc *) NULL,	Tcl_CatchObjCmd,	 -        TclCompileCatchCmd,		1}, -    {"clock",		(Tcl_CmdProc *) NULL,	Tcl_ClockObjCmd, -        (CompileProc *) NULL,		1}, -    {"concat",		(Tcl_CmdProc *) NULL,	Tcl_ConcatObjCmd, -        (CompileProc *) NULL,		1}, -    {"continue",	(Tcl_CmdProc *) NULL,	Tcl_ContinueObjCmd, -        TclCompileContinueCmd,		1}, -    {"encoding",	(Tcl_CmdProc *) NULL,	Tcl_EncodingObjCmd, -        (CompileProc *) NULL,		0}, -    {"error",		(Tcl_CmdProc *) NULL,	Tcl_ErrorObjCmd, -        (CompileProc *) NULL,		1}, -    {"eval",		(Tcl_CmdProc *) NULL,	Tcl_EvalObjCmd, -        (CompileProc *) NULL,		1}, -    {"exit",		(Tcl_CmdProc *) NULL,	Tcl_ExitObjCmd, -        (CompileProc *) NULL,		0}, -    {"expr",		(Tcl_CmdProc *) NULL,	Tcl_ExprObjCmd, -        TclCompileExprCmd,		1}, -    {"fcopy",		(Tcl_CmdProc *) NULL,	Tcl_FcopyObjCmd, -        (CompileProc *) NULL,		1}, -    {"fileevent",	(Tcl_CmdProc *) NULL,	Tcl_FileEventObjCmd, -        (CompileProc *) NULL,		1}, -    {"for",		(Tcl_CmdProc *) NULL,	Tcl_ForObjCmd, -        TclCompileForCmd,		1}, -    {"foreach",		(Tcl_CmdProc *) NULL,	Tcl_ForeachObjCmd, -        TclCompileForeachCmd,		1}, -    {"format",		(Tcl_CmdProc *) NULL,	Tcl_FormatObjCmd, -        (CompileProc *) NULL,		1}, -    {"global",		(Tcl_CmdProc *) NULL,	Tcl_GlobalObjCmd, -        (CompileProc *) NULL,		1}, -    {"if",		(Tcl_CmdProc *) NULL,	Tcl_IfObjCmd, -        TclCompileIfCmd,		1}, -    {"incr",		(Tcl_CmdProc *) NULL,	Tcl_IncrObjCmd, -        TclCompileIncrCmd,		1}, -    {"info",		(Tcl_CmdProc *) NULL,	Tcl_InfoObjCmd, -        (CompileProc *) NULL,		1}, -    {"join",		(Tcl_CmdProc *) NULL,	Tcl_JoinObjCmd, -        (CompileProc *) NULL,		1}, -    {"lappend",		(Tcl_CmdProc *) NULL,	Tcl_LappendObjCmd, -        TclCompileLappendCmd,		1}, -    {"lindex",		(Tcl_CmdProc *) NULL,	Tcl_LindexObjCmd, -        TclCompileLindexCmd,		1}, -    {"linsert",		(Tcl_CmdProc *) NULL,	Tcl_LinsertObjCmd, -        (CompileProc *) NULL,		1}, -    {"list",		(Tcl_CmdProc *) NULL,	Tcl_ListObjCmd, -        TclCompileListCmd,		1}, -    {"llength",		(Tcl_CmdProc *) NULL,	Tcl_LlengthObjCmd, -        TclCompileLlengthCmd,		1}, -    {"load",		(Tcl_CmdProc *) NULL,	Tcl_LoadObjCmd, -        (CompileProc *) NULL,		0}, -    {"lrange",		(Tcl_CmdProc *) NULL,	Tcl_LrangeObjCmd, -        (CompileProc *) NULL,		1}, -    {"lreplace",	(Tcl_CmdProc *) NULL,	Tcl_LreplaceObjCmd, -        (CompileProc *) NULL,		1}, -    {"lsearch",		(Tcl_CmdProc *) NULL,	Tcl_LsearchObjCmd, -        (CompileProc *) NULL,		1}, -    {"lset",            (Tcl_CmdProc *) NULL,   Tcl_LsetObjCmd, -        TclCompileLsetCmd,           	1}, -    {"lsort",		(Tcl_CmdProc *) NULL,	Tcl_LsortObjCmd, -        (CompileProc *) NULL,		1}, -    {"namespace",	(Tcl_CmdProc *) NULL,	Tcl_NamespaceObjCmd, -        (CompileProc *) NULL,		1}, -    {"package",		(Tcl_CmdProc *) NULL,	Tcl_PackageObjCmd, -        (CompileProc *) NULL,		1}, -    {"proc",		(Tcl_CmdProc *) NULL,	Tcl_ProcObjCmd,	 -        (CompileProc *) NULL,		1}, -    {"regexp",		(Tcl_CmdProc *) NULL,	Tcl_RegexpObjCmd, -        TclCompileRegexpCmd,		1}, -    {"regsub",		(Tcl_CmdProc *) NULL,	Tcl_RegsubObjCmd, -        (CompileProc *) NULL,		1}, -    {"rename",		(Tcl_CmdProc *) NULL,	Tcl_RenameObjCmd, -        (CompileProc *) NULL,		1}, -    {"return",		(Tcl_CmdProc *) NULL,	Tcl_ReturnObjCmd,	 -        TclCompileReturnCmd,		1}, -    {"scan",		(Tcl_CmdProc *) NULL,	Tcl_ScanObjCmd, -        (CompileProc *) NULL,		1}, -    {"set",		(Tcl_CmdProc *) NULL,	Tcl_SetObjCmd, -        TclCompileSetCmd,		1}, -    {"split",		(Tcl_CmdProc *) NULL,	Tcl_SplitObjCmd, -        (CompileProc *) NULL,		1}, -    {"string",		(Tcl_CmdProc *) NULL,	Tcl_StringObjCmd, -        TclCompileStringCmd,		1}, -    {"subst",		(Tcl_CmdProc *) NULL,	Tcl_SubstObjCmd, -        (CompileProc *) NULL,		1}, -    {"switch",		(Tcl_CmdProc *) NULL,	Tcl_SwitchObjCmd,	 -        (CompileProc *) NULL,		1}, -    {"trace",		(Tcl_CmdProc *) NULL,	Tcl_TraceObjCmd, -        (CompileProc *) NULL,		1}, -    {"unset",		(Tcl_CmdProc *) NULL,	Tcl_UnsetObjCmd,	 -        (CompileProc *) NULL,		1}, -    {"uplevel",		(Tcl_CmdProc *) NULL,	Tcl_UplevelObjCmd,	 -        (CompileProc *) NULL,		1}, -    {"upvar",		(Tcl_CmdProc *) NULL,	Tcl_UpvarObjCmd,	 -        (CompileProc *) NULL,		1}, -    {"variable",	(Tcl_CmdProc *) NULL,	Tcl_VariableObjCmd, -        (CompileProc *) NULL,		1}, -    {"while",		(Tcl_CmdProc *) NULL,	Tcl_WhileObjCmd, -        TclCompileWhileCmd,		1}, - -    /* -     * Commands in the UNIX core: -     */ - -#ifndef TCL_GENERIC_ONLY -    {"after",		(Tcl_CmdProc *) NULL,	Tcl_AfterObjCmd, -        (CompileProc *) NULL,		1}, -    {"cd",		(Tcl_CmdProc *) NULL,	Tcl_CdObjCmd, -        (CompileProc *) NULL,		0}, -    {"close",		(Tcl_CmdProc *) NULL,	Tcl_CloseObjCmd, -        (CompileProc *) NULL,		1}, -    {"eof",		(Tcl_CmdProc *) NULL,	Tcl_EofObjCmd, -        (CompileProc *) NULL,		1}, -    {"fblocked",	(Tcl_CmdProc *) NULL,	Tcl_FblockedObjCmd, -        (CompileProc *) NULL,		1}, -    {"fconfigure",	(Tcl_CmdProc *) NULL,	Tcl_FconfigureObjCmd, -        (CompileProc *) NULL,		0}, -    {"file",		(Tcl_CmdProc *) NULL,	Tcl_FileObjCmd, -        (CompileProc *) NULL,		0}, -    {"flush",		(Tcl_CmdProc *) NULL,	Tcl_FlushObjCmd, -        (CompileProc *) NULL,		1}, -    {"gets",		(Tcl_CmdProc *) NULL,	Tcl_GetsObjCmd, -        (CompileProc *) NULL,		1}, -    {"glob",		(Tcl_CmdProc *) NULL,	Tcl_GlobObjCmd, -        (CompileProc *) NULL,		0}, -    {"open",		(Tcl_CmdProc *) NULL,	Tcl_OpenObjCmd, -        (CompileProc *) NULL,		0}, -    {"pid",		(Tcl_CmdProc *) NULL,	Tcl_PidObjCmd, -        (CompileProc *) NULL,		1}, -    {"puts",		(Tcl_CmdProc *) NULL,	Tcl_PutsObjCmd, -        (CompileProc *) NULL,		1}, -    {"pwd",		(Tcl_CmdProc *) NULL,	Tcl_PwdObjCmd, -        (CompileProc *) NULL,		0}, -    {"read",		(Tcl_CmdProc *) NULL,	Tcl_ReadObjCmd, -        (CompileProc *) NULL,		1}, -    {"seek",		(Tcl_CmdProc *) NULL,	Tcl_SeekObjCmd, -        (CompileProc *) NULL,		1}, -    {"socket",		(Tcl_CmdProc *) NULL,	Tcl_SocketObjCmd, -        (CompileProc *) NULL,		0}, -    {"tell",		(Tcl_CmdProc *) NULL,	Tcl_TellObjCmd, -        (CompileProc *) NULL,		1}, -    {"time",		(Tcl_CmdProc *) NULL,	Tcl_TimeObjCmd, -        (CompileProc *) NULL,		1}, -    {"update",		(Tcl_CmdProc *) NULL,	Tcl_UpdateObjCmd, -        (CompileProc *) NULL,		1}, -    {"vwait",		(Tcl_CmdProc *) NULL,	Tcl_VwaitObjCmd, -        (CompileProc *) NULL,		1}, -     -#ifdef MAC_TCL -    {"beep",		(Tcl_CmdProc *) NULL,	Tcl_BeepObjCmd, -        (CompileProc *) NULL,		0}, -    {"echo",		Tcl_EchoCmd,		(Tcl_ObjCmdProc *) NULL, -        (CompileProc *) NULL,		0}, -    {"ls",		(Tcl_CmdProc *) NULL, 	Tcl_LsObjCmd, -        (CompileProc *) NULL,		0}, -    {"resource",	(Tcl_CmdProc *) NULL,	Tcl_ResourceObjCmd, -        (CompileProc *) NULL,		1}, -    {"source",		(Tcl_CmdProc *) NULL,	Tcl_MacSourceObjCmd, -        (CompileProc *) NULL,		0}, -#else -    {"exec",		(Tcl_CmdProc *) NULL,	Tcl_ExecObjCmd, -        (CompileProc *) NULL,		0}, -    {"source",		(Tcl_CmdProc *) NULL,	Tcl_SourceObjCmd, -        (CompileProc *) NULL,		0}, -#endif /* MAC_TCL */ -     -#endif /* TCL_GENERIC_ONLY */ -    {NULL,		(Tcl_CmdProc *) NULL,	(Tcl_ObjCmdProc *) NULL, -        (CompileProc *) NULL,		0} +static const CmdInfo builtInCmds[] = { +    /* +     * Commands in the generic core. +     */ + +    {"append",		Tcl_AppendObjCmd,	TclCompileAppendCmd,	NULL,	CMD_IS_SAFE}, +    {"apply",		Tcl_ApplyObjCmd,	NULL,			TclNRApplyObjCmd,	CMD_IS_SAFE}, +    {"break",		Tcl_BreakObjCmd,	TclCompileBreakCmd,	NULL,	CMD_IS_SAFE}, +#ifndef EXCLUDE_OBSOLETE_COMMANDS +    {"case",		Tcl_CaseObjCmd,		NULL,			NULL,	CMD_IS_SAFE}, +#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}, +    {"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}, +    {"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}, +    {"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}, +    {"lrange",		Tcl_LrangeObjCmd,	TclCompileLrangeCmd,	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}, +    {"lset",		Tcl_LsetObjCmd,		TclCompileLsetCmd,	NULL,	CMD_IS_SAFE}, +    {"lsort",		Tcl_LsortObjCmd,	NULL,			NULL,	CMD_IS_SAFE}, +    {"package",		Tcl_PackageObjCmd,	NULL,			NULL,	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}, + +    /* +     * 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}, +    {"encoding",	Tcl_EncodingObjCmd,	NULL,			NULL,	0}, +    {"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}, +    {"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}  };  /* - * The following structure holds the client data for string-based - * trace procs + * Math functions. All are safe.   */ -typedef struct StringTraceData { -    ClientData clientData;	/* Client data from Tcl_CreateTrace */ -    Tcl_CmdTraceProc* proc;	/* Trace procedure from Tcl_CreateTrace */ -} StringTraceData; +typedef struct { +    const char *name;		/* Name of the function. The full name is +				 * "::tcl::mathfunc::<name>". */ +    Tcl_ObjCmdProc *objCmdProc;	/* Function that evaluates the function */ +    ClientData clientData;	/* Client data for the function */ +} BuiltinFuncDef; +static const BuiltinFuncDef BuiltinFuncTable[] = { +    { "abs",	ExprAbsFunc,	NULL			}, +    { "acos",	ExprUnaryFunc,	(ClientData) acos	}, +    { "asin",	ExprUnaryFunc,	(ClientData) asin	}, +    { "atan",	ExprUnaryFunc,	(ClientData) atan	}, +    { "atan2",	ExprBinaryFunc,	(ClientData) atan2	}, +    { "bool",	ExprBoolFunc,	NULL			}, +    { "ceil",	ExprCeilFunc,	NULL			}, +    { "cos",	ExprUnaryFunc,	(ClientData) cos	}, +    { "cosh",	ExprUnaryFunc,	(ClientData) cosh	}, +    { "double",	ExprDoubleFunc,	NULL			}, +    { "entier",	ExprEntierFunc,	NULL			}, +    { "exp",	ExprUnaryFunc,	(ClientData) exp	}, +    { "floor",	ExprFloorFunc,	NULL			}, +    { "fmod",	ExprBinaryFunc,	(ClientData) fmod	}, +    { "hypot",	ExprBinaryFunc,	(ClientData) hypot	}, +    { "int",	ExprIntFunc,	NULL			}, +    { "isqrt",	ExprIsqrtFunc,	NULL			}, +    { "log",	ExprUnaryFunc,	(ClientData) log	}, +    { "log10",	ExprUnaryFunc,	(ClientData) log10	}, +    { "pow",	ExprBinaryFunc,	(ClientData) pow	}, +    { "rand",	ExprRandFunc,	NULL			}, +    { "round",	ExprRoundFunc,	NULL			}, +    { "sin",	ExprUnaryFunc,	(ClientData) sin	}, +    { "sinh",	ExprUnaryFunc,	(ClientData) sinh	}, +    { "sqrt",	ExprSqrtFunc,	NULL			}, +    { "srand",	ExprSrandFunc,	NULL			}, +    { "tan",	ExprUnaryFunc,	(ClientData) tan	}, +    { "tanh",	ExprUnaryFunc,	(ClientData) tanh	}, +    { "wide",	ExprWideFunc,	NULL			}, +    { NULL, NULL, NULL } +}; + +/* + * TIP#174's math operators. All are safe. + */ + +typedef struct { +    const char *name;		/* Name of object-based command. */ +    Tcl_ObjCmdProc *objProc;	/* Object-based function for command. */ +    CompileProc *compileProc;	/* Function called to compile command. */ +    union { +	int numArgs; +	int identity; +    } i; +    const char *expected;	/* For error message, what argument(s) +				 * were expected. */ +} OpCmdInfo; +static const OpCmdInfo mathOpCmds[] = { +    { "~",	TclSingleOpCmd,		TclCompileInvertOpCmd, +		/* numArgs */ {1},	"integer"}, +    { "!",	TclSingleOpCmd,		TclCompileNotOpCmd, +		/* numArgs */ {1},	"boolean"}, +    { "+",	TclVariadicOpCmd,	TclCompileAddOpCmd, +		/* identity */ {0},	NULL}, +    { "*",	TclVariadicOpCmd,	TclCompileMulOpCmd, +		/* identity */ {1},	NULL}, +    { "&",	TclVariadicOpCmd,	TclCompileAndOpCmd, +		/* identity */ {-1},	NULL}, +    { "|",	TclVariadicOpCmd,	TclCompileOrOpCmd, +		/* identity */ {0},	NULL}, +    { "^",	TclVariadicOpCmd,	TclCompileXorOpCmd, +		/* identity */ {0},	NULL}, +    { "**",	TclVariadicOpCmd,	TclCompilePowOpCmd, +		/* identity */ {1},	NULL}, +    { "<<",	TclSingleOpCmd,		TclCompileLshiftOpCmd, +		/* numArgs */ {2},	"integer shift"}, +    { ">>",	TclSingleOpCmd,		TclCompileRshiftOpCmd, +		/* numArgs */ {2},	"integer shift"}, +    { "%",	TclSingleOpCmd,		TclCompileModOpCmd, +		/* numArgs */ {2},	"integer integer"}, +    { "!=",	TclSingleOpCmd,		TclCompileNeqOpCmd, +		/* numArgs */ {2},	"value value"}, +    { "ne",	TclSingleOpCmd,		TclCompileStrneqOpCmd, +		/* numArgs */ {2},	"value value"}, +    { "in",	TclSingleOpCmd,		TclCompileInOpCmd, +		/* numArgs */ {2},	"value list"}, +    { "ni",	TclSingleOpCmd,		TclCompileNiOpCmd, +		/* numArgs */ {2},	"value list"}, +    { "-",	TclNoIdentOpCmd,	TclCompileMinusOpCmd, +		/* unused */ {0},	"value ?value ...?"}, +    { "/",	TclNoIdentOpCmd,	TclCompileDivOpCmd, +		/* unused */ {0},	"value ?value ...?"}, +    { "<",	TclSortingOpCmd,	TclCompileLessOpCmd, +		/* unused */ {0},	NULL}, +    { "<=",	TclSortingOpCmd,	TclCompileLeqOpCmd, +		/* unused */ {0},	NULL}, +    { ">",	TclSortingOpCmd,	TclCompileGreaterOpCmd, +		/* unused */ {0},	NULL}, +    { ">=",	TclSortingOpCmd,	TclCompileGeqOpCmd, +		/* unused */ {0},	NULL}, +    { "==",	TclSortingOpCmd,	TclCompileEqOpCmd, +		/* unused */ {0},	NULL}, +    { "eq",	TclSortingOpCmd,	TclCompileStreqOpCmd, +		/* unused */ {0},	NULL}, +    { NULL,	NULL,			NULL, +		{0},			NULL} +}; + +/* + *---------------------------------------------------------------------- + * + * TclFinalizeEvaluation -- + * + *	Finalizes the script cancellation hash table. + * + * Results: + *	None. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +void +TclFinalizeEvaluation(void) +{ +    Tcl_MutexLock(&cancelLock); +    if (cancelTableInitialized == 1) { +	Tcl_DeleteHashTable(&cancelTable); +	cancelTableInitialized = 0; +    } +    Tcl_MutexUnlock(&cancelLock); +}  /*   *---------------------------------------------------------------------- @@ -285,28 +440,29 @@ typedef struct StringTraceData {   *	Create a new TCL command interpreter.   *   * Results: - *	The return value is a token for the interpreter, which may be - *	used in calls to procedures like Tcl_CreateCmd, Tcl_Eval, or - *	Tcl_DeleteInterp. + *	The return value is a token for the interpreter, which may be used in + *	calls to functions like Tcl_CreateCmd, Tcl_Eval, or Tcl_DeleteInterp.   *   * Side effects: - *	The command interpreter is initialized with the built-in commands - *      and with the variables documented in tclvars(n). + *	The command interpreter is initialized with the built-in commands and + *	with the variables documented in tclvars(n).   *   *----------------------------------------------------------------------   */  Tcl_Interp * -Tcl_CreateInterp() +Tcl_CreateInterp(void)  {      Interp *iPtr;      Tcl_Interp *interp;      Command *cmdPtr; -    BuiltinFunc *builtinFuncPtr; -    MathFunc *mathFuncPtr; +    const BuiltinFuncDef *builtinFuncPtr; +    const OpCmdInfo *opcmdInfoPtr; +    const CmdInfo *cmdInfoPtr; +    Tcl_Namespace *mathfuncNSPtr, *mathopNSPtr;      Tcl_HashEntry *hPtr; -    CmdInfo *cmdInfoPtr; -    int i; +    int isNew; +    CancelInfo *cancelInfo;      union {  	char c[sizeof(short)];  	short s; @@ -314,64 +470,111 @@ Tcl_CreateInterp()  #ifdef TCL_COMPILE_STATS      ByteCodeStats *statsPtr;  #endif /* TCL_COMPILE_STATS */ +    char mathFuncName[32]; +    CallFrame *framePtr; +    int result; -    TclInitSubsystems(NULL); +    TclInitSubsystems();      /* -     * Panic if someone updated the CallFrame structure without -     * also updating the Tcl_CallFrame structure (or vice versa). -     */   +     * Panic if someone updated the CallFrame structure without also updating +     * the Tcl_CallFrame structure (or vice versa). +     */ -    if (sizeof(Tcl_CallFrame) != sizeof(CallFrame)) { +    if (sizeof(Tcl_CallFrame) < sizeof(CallFrame)) {  	/*NOTREACHED*/ -        panic("Tcl_CallFrame and CallFrame are not the same size"); +	Tcl_Panic("Tcl_CallFrame must not be smaller than CallFrame"); +    } + +#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);      }      /*       * Initialize support for namespaces and create the global namespace -     * (whose name is ""; an alias is "::"). This also initializes the -     * Tcl object type table and other object management code. +     * (whose name is ""; an alias is "::"). This also initializes the Tcl +     * object type table and other object management code.       */ -    iPtr = (Interp *) ckalloc(sizeof(Interp)); +    iPtr = ckalloc(sizeof(Interp));      interp = (Tcl_Interp *) iPtr; -    iPtr->result		= iPtr->resultSpace; -    iPtr->freeProc		= NULL; -    iPtr->errorLine		= 0; -    iPtr->objResultPtr		= Tcl_NewObj(); +    iPtr->result = iPtr->resultSpace; +    iPtr->freeProc = NULL; +    iPtr->errorLine = 0; +    iPtr->objResultPtr = Tcl_NewObj();      Tcl_IncrRefCount(iPtr->objResultPtr); -    iPtr->handle		= TclHandleCreate(iPtr); -    iPtr->globalNsPtr		= NULL; -    iPtr->hiddenCmdTablePtr	= NULL; -    iPtr->interpInfo		= NULL; -    Tcl_InitHashTable(&iPtr->mathFuncTable, TCL_STRING_KEYS); +    iPtr->handle = TclHandleCreate(iPtr); +    iPtr->globalNsPtr = NULL; +    iPtr->hiddenCmdTablePtr = NULL; +    iPtr->interpInfo = NULL; + +    TCL_CT_ASSERT(sizeof(iPtr->extra) <= sizeof(Tcl_HashTable)); +    iPtr->extra.optimizer = TclOptimizeBytecode;      iPtr->numLevels = 0;      iPtr->maxNestingDepth = MAX_NESTING_DEPTH; -    iPtr->framePtr = NULL; -    iPtr->varFramePtr = NULL; +    iPtr->framePtr = NULL;	/* Initialise as soon as :: is available */ +    iPtr->varFramePtr = NULL;	/* Initialise as soon as :: is available */ -#ifdef TCL_TIP280      /* -     * TIP #280 - Initialize the arrays used to extend the ByteCode and -     * Proc structures. +     * TIP #280 - Initialize the arrays used to extend the ByteCode and Proc +     * structures.       */ -    iPtr->cmdFramePtr  = NULL; -    iPtr->linePBodyPtr = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); -    iPtr->lineBCPtr    = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); -    iPtr->lineLAPtr    = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); -    iPtr->lineLABCPtr  = (Tcl_HashTable*) ckalloc (sizeof (Tcl_HashTable)); + +    iPtr->cmdFramePtr = NULL; +    iPtr->linePBodyPtr = ckalloc(sizeof(Tcl_HashTable)); +    iPtr->lineBCPtr = ckalloc(sizeof(Tcl_HashTable)); +    iPtr->lineLAPtr = ckalloc(sizeof(Tcl_HashTable)); +    iPtr->lineLABCPtr = ckalloc(sizeof(Tcl_HashTable));      Tcl_InitHashTable(iPtr->linePBodyPtr, TCL_ONE_WORD_KEYS); -    Tcl_InitHashTable(iPtr->lineBCPtr,    TCL_ONE_WORD_KEYS); -    Tcl_InitHashTable(iPtr->lineLAPtr,    TCL_ONE_WORD_KEYS); -    Tcl_InitHashTable(iPtr->lineLABCPtr,  TCL_ONE_WORD_KEYS); -#endif +    Tcl_InitHashTable(iPtr->lineBCPtr, TCL_ONE_WORD_KEYS); +    Tcl_InitHashTable(iPtr->lineLAPtr, TCL_ONE_WORD_KEYS); +    Tcl_InitHashTable(iPtr->lineLABCPtr, TCL_ONE_WORD_KEYS); +    iPtr->scriptCLLocPtr = NULL;      iPtr->activeVarTracePtr = NULL; -    iPtr->returnCode = TCL_OK; + +    iPtr->returnOpts = NULL;      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); +    iPtr->returnLevel = 1; +    iPtr->returnCode = TCL_OK; + +    iPtr->rootFramePtr = NULL;	/* Initialise as soon as :: is available */ +    iPtr->lookupNsPtr = NULL;      iPtr->appendResult = NULL;      iPtr->appendAvl = 0; @@ -379,15 +582,16 @@ Tcl_CreateInterp()      Tcl_InitHashTable(&iPtr->packageTable, TCL_STRING_KEYS);      iPtr->packageUnknown = NULL; -#ifdef TCL_TIP268 +      /* TIP #268 */ -    iPtr->packagePrefer = (getenv ("TCL_PKG_PREFER_LATEST") == NULL ?  -			   PKG_PREFER_STABLE   : -			   PKG_PREFER_LATEST); -#endif +    if (getenv("TCL_PKG_PREFER_LATEST") == NULL) { +	iPtr->packagePrefer = PKG_PREFER_STABLE; +    } else { +	iPtr->packagePrefer = PKG_PREFER_LATEST; +    } +      iPtr->cmdCount = 0; -    iPtr->termOffset = 0; -    TclInitLiteralTable(&(iPtr->literalTable)); +    TclInitLiteralTable(&iPtr->literalTable);      iPtr->compileEpoch = 0;      iPtr->compiledProcPtr = NULL;      iPtr->resolverPtr = NULL; @@ -398,28 +602,90 @@ Tcl_CreateInterp()      iPtr->tracesForbiddingInline = 0;      iPtr->activeCmdTracePtr = NULL;      iPtr->activeInterpTracePtr = NULL; -    iPtr->assocData = (Tcl_HashTable *) NULL; -    iPtr->execEnvPtr = NULL;	      /* set after namespaces initialized */ -    iPtr->emptyObjPtr = Tcl_NewObj(); /* another empty object */ +    iPtr->assocData = NULL; +    iPtr->execEnvPtr = NULL;	/* Set after namespaces initialized. */ +    iPtr->emptyObjPtr = Tcl_NewObj(); +				/* Another empty object. */      Tcl_IncrRefCount(iPtr->emptyObjPtr);      iPtr->resultSpace[0] = 0;      iPtr->threadId = Tcl_GetCurrentThread(); -    iPtr->globalNsPtr = NULL;	/* force creation of global ns below */ +    /* TIP #378 */ +#ifdef TCL_INTERP_DEBUG_FRAME +    iPtr->flags |= INTERP_DEBUG_FRAME; +#else +    if (getenv("TCL_INTERP_DEBUG_FRAME") != NULL) { +        iPtr->flags |= INTERP_DEBUG_FRAME; +    } +#endif + +    /* +     * Initialise the tables for variable traces and searches *before* +     * creating the global ns - so that the trace on errorInfo can be +     * recorded. +     */ + +    Tcl_InitHashTable(&iPtr->varTraces, TCL_ONE_WORD_KEYS); +    Tcl_InitHashTable(&iPtr->varSearches, TCL_ONE_WORD_KEYS); + +    iPtr->globalNsPtr = NULL;	/* Force creation of global ns below. */      iPtr->globalNsPtr = (Namespace *) Tcl_CreateNamespace(interp, "", -	    (ClientData) NULL, (Tcl_NamespaceDeleteProc *) NULL); +	    NULL, NULL);      if (iPtr->globalNsPtr == NULL) { -        panic("Tcl_CreateInterp: can't create global namespace"); +	Tcl_Panic("Tcl_CreateInterp: can't create global namespace");      }      /* +     * Initialise the rootCallframe. It cannot be allocated on the stack, as +     * it has to be in place before TclCreateExecEnv tries to use a variable. +     */ + +    /* This is needed to satisfy GCC 3.3's strict aliasing rules */ +    framePtr = ckalloc(sizeof(CallFrame)); +    result = Tcl_PushCallFrame(interp, (Tcl_CallFrame *) framePtr, +	    (Tcl_Namespace *) iPtr->globalNsPtr, /*isProcCallFrame*/ 0); +    if (result != TCL_OK) { +	Tcl_Panic("Tcl_CreateInterp: failed to push the root stack frame"); +    } +    framePtr->objc = 0; + +    iPtr->framePtr = framePtr; +    iPtr->varFramePtr = framePtr; +    iPtr->rootFramePtr = framePtr; + +    /*       * Initialize support for code compilation and execution. We call       * TclCreateExecEnv after initializing namespaces since it tries to       * reference a Tcl variable (it links to the Tcl "tcl_traceExec"       * variable).       */ -    iPtr->execEnvPtr = TclCreateExecEnv(interp); +    iPtr->execEnvPtr = TclCreateExecEnv(interp, INTERP_STACK_INITIAL_SIZE); + +    /* +     * TIP #219, Tcl Channel Reflection API support. +     */ + +    iPtr->chanMsg = NULL; + +    /* +     * TIP #285, Script cancellation support. +     */ + +    iPtr->asyncCancelMsg = Tcl_NewObj(); + +    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 @@ -427,35 +693,32 @@ Tcl_CreateInterp()       */  #ifdef TCL_COMPILE_STATS -    statsPtr = &(iPtr->stats); +    statsPtr = &iPtr->stats;      statsPtr->numExecutions = 0;      statsPtr->numCompilations = 0;      statsPtr->numByteCodesFreed = 0; -    (VOID *) memset(statsPtr->instructionCount, 0, +    memset(statsPtr->instructionCount, 0,  	    sizeof(statsPtr->instructionCount));      statsPtr->totalSrcBytes = 0.0;      statsPtr->totalByteCodeBytes = 0.0;      statsPtr->currentSrcBytes = 0.0;      statsPtr->currentByteCodeBytes = 0.0; -    (VOID *) memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); -    (VOID *) memset(statsPtr->byteCodeCount, 0, -	    sizeof(statsPtr->byteCodeCount)); -    (VOID *) memset(statsPtr->lifetimeCount, 0, -	    sizeof(statsPtr->lifetimeCount)); -     -    statsPtr->currentInstBytes   = 0.0; -    statsPtr->currentLitBytes    = 0.0; +    memset(statsPtr->srcCount, 0, sizeof(statsPtr->srcCount)); +    memset(statsPtr->byteCodeCount, 0, sizeof(statsPtr->byteCodeCount)); +    memset(statsPtr->lifetimeCount, 0, sizeof(statsPtr->lifetimeCount)); + +    statsPtr->currentInstBytes = 0.0; +    statsPtr->currentLitBytes = 0.0;      statsPtr->currentExceptBytes = 0.0; -    statsPtr->currentAuxBytes    = 0.0; +    statsPtr->currentAuxBytes = 0.0;      statsPtr->currentCmdMapBytes = 0.0; -     -    statsPtr->numLiteralsCreated    = 0; -    statsPtr->totalLitStringBytes   = 0.0; + +    statsPtr->numLiteralsCreated = 0; +    statsPtr->totalLitStringBytes = 0.0;      statsPtr->currentLitStringBytes = 0.0; -    (VOID *) memset(statsPtr->literalCount, 0, -            sizeof(statsPtr->literalCount)); -#endif /* TCL_COMPILE_STATS */     +    memset(statsPtr->literalCount, 0, sizeof(statsPtr->literalCount)); +#endif /* TCL_COMPILE_STATS */      /*       * Initialise the stub table pointer. @@ -463,61 +726,133 @@ Tcl_CreateInterp()      iPtr->stubTable = &tclStubs; -     +    /* +     * Initialize the ensemble error message rewriting support. +     */ + +    iPtr->ensembleRewrite.sourceObjs = NULL; +    iPtr->ensembleRewrite.numRemovedObjs = 0; +    iPtr->ensembleRewrite.numInsertedObjs = 0; + +    /* +     * TIP#143: Initialise the resource limit support. +     */ + +    TclInitLimitSupport(interp); + +    /* +     * Initialise the thread-specific data ekeko. Note that the thread's alloc +     * cache was already initialised by the call to alloc the interp struct. +     */ + +#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_CreateCommand, because it's faster (there's no need to check for -     * a pre-existing command by the same name). If a command has a -     * Tcl_CmdProc but no Tcl_ObjCmdProc, set the Tcl_ObjCmdProc to -     * TclInvokeStringCommand. This is an object-based wrapper procedure -     * that extracts strings, calls the string procedure, and creates an -     * object for the result. Similarly, if a command has a Tcl_ObjCmdProc -     * but no Tcl_CmdProc, set the Tcl_CmdProc to TclInvokeObjectCommand. -     */ - -    for (cmdInfoPtr = builtInCmds;  cmdInfoPtr->name != NULL; -	    cmdInfoPtr++) { -	int new; -	Tcl_HashEntry *hPtr; - -	if ((cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) -	        && (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) -	        && (cmdInfoPtr->compileProc == (CompileProc *) NULL)) { -	    panic("Tcl_CreateInterp: builtin command with NULL string and object command procs and a NULL compile proc\n"); +     * 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++) { +	if ((cmdInfoPtr->objProc == NULL) +		&& (cmdInfoPtr->compileProc == NULL) +		&& (cmdInfoPtr->nreProc == NULL)) { +	    Tcl_Panic("builtin command with NULL object command proc and a NULL compile proc");  	} -	 +  	hPtr = Tcl_CreateHashEntry(&iPtr->globalNsPtr->cmdTable, -	        cmdInfoPtr->name, &new); -	if (new) { -	    cmdPtr = (Command *) ckalloc(sizeof(Command)); +		cmdInfoPtr->name, &isNew); +	if (isNew) { +	    cmdPtr = ckalloc(sizeof(Command));  	    cmdPtr->hPtr = hPtr;  	    cmdPtr->nsPtr = iPtr->globalNsPtr;  	    cmdPtr->refCount = 1;  	    cmdPtr->cmdEpoch = 0;  	    cmdPtr->compileProc = cmdInfoPtr->compileProc; -	    if (cmdInfoPtr->proc == (Tcl_CmdProc *) NULL) { -		cmdPtr->proc = TclInvokeObjectCommand; -		cmdPtr->clientData = (ClientData) cmdPtr; -	    } else { -		cmdPtr->proc = cmdInfoPtr->proc; -		cmdPtr->clientData = (ClientData) NULL; -	    } -	    if (cmdInfoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { -		cmdPtr->objProc = TclInvokeStringCommand; -		cmdPtr->objClientData = (ClientData) cmdPtr; -	    } else { -		cmdPtr->objProc = cmdInfoPtr->objProc; -		cmdPtr->objClientData = (ClientData) NULL; -	    } +	    cmdPtr->proc = TclInvokeObjectCommand; +	    cmdPtr->clientData = cmdPtr; +	    cmdPtr->objProc = cmdInfoPtr->objProc; +	    cmdPtr->objClientData = NULL;  	    cmdPtr->deleteProc = NULL; -	    cmdPtr->deleteData = (ClientData) NULL; +	    cmdPtr->deleteData = NULL;  	    cmdPtr->flags = 0; +            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", "dict", "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 "file". +     */ + +    TclInitArrayCmd(interp); +    TclInitBinaryCmd(interp); +    TclInitChanCmd(interp); +    TclInitDictCmd(interp); +    TclInitFileCmd(interp); +    TclInitInfoCmd(interp); +    TclInitNamespaceCmd(interp); +    TclInitStringCmd(interp); +    TclInitPrefixCmd(interp); + +    /* +     * Register "clock" subcommands. These *do* go through +     * Tcl_CreateObjCommand, since they aren't in the global namespace and +     * involve ensembles. +     */ + +    TclClockInit(interp); + +    /* +     * Register the built-in functions. This is empty now that they are +     * implemented as commands in the ::tcl::mathfunc namespace. +     */ + +    /* +     * Register the default [interp bgerror] handler. +     */ + +    Tcl_CreateObjCommand(interp, "::tcl::Bgerror", +	    TclDefaultBgErrorHandlerObjCmd, NULL, NULL); + +    /* +     * Create unsupported commands for debugging bytecode and objects. +     */ + +    Tcl_CreateObjCommand(interp, "::tcl::unsupported::disassemble", +	    Tcl_DisassembleObjCmd, NULL, 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; + +    Tcl_NRCreateCommand(interp, "::tcl::unsupported::inject", NULL, +	    NRCoroInjectObjCmd, NULL, NULL); +  #ifdef USE_DTRACE      /*       * Register the tcl::dtrace command. @@ -530,57 +865,60 @@ Tcl_CreateInterp()       * Register the builtin math functions.       */ -    i = 0; -    for (builtinFuncPtr = tclBuiltinFuncTable;  builtinFuncPtr->name != NULL; +    mathfuncNSPtr = Tcl_CreateNamespace(interp, "::tcl::mathfunc", NULL,NULL); +    if (mathfuncNSPtr == NULL) { +	Tcl_Panic("Can't create math function namespace"); +    } +#define MATH_FUNC_PREFIX_LEN 17 /* == strlen("::tcl::mathfunc::") */ +    memcpy(mathFuncName, "::tcl::mathfunc::", MATH_FUNC_PREFIX_LEN); +    for (builtinFuncPtr = BuiltinFuncTable; builtinFuncPtr->name != NULL;  	    builtinFuncPtr++) { -	Tcl_CreateMathFunc((Tcl_Interp *) iPtr, builtinFuncPtr->name, -		builtinFuncPtr->numArgs, builtinFuncPtr->argTypes, -		(Tcl_MathProc *) NULL, (ClientData) 0); -	hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, -		builtinFuncPtr->name); -	if (hPtr == NULL) { -	    panic("Tcl_CreateInterp: Tcl_CreateMathFunc incorrectly registered '%s'", builtinFuncPtr->name); -	    return NULL; +	strcpy(mathFuncName+MATH_FUNC_PREFIX_LEN, builtinFuncPtr->name); +	Tcl_CreateObjCommand(interp, mathFuncName, +		builtinFuncPtr->objCmdProc, builtinFuncPtr->clientData, NULL); +	Tcl_Export(interp, mathfuncNSPtr, builtinFuncPtr->name, 0); +    } + +    /* +     * Register the mathematical "operator" commands. [TIP #174] +     */ + +    mathopNSPtr = Tcl_CreateNamespace(interp, "::tcl::mathop", NULL, NULL); +    if (mathopNSPtr == NULL) { +	Tcl_Panic("can't create math operator namespace"); +    } +    Tcl_Export(interp, mathopNSPtr, "*", 1); +#define MATH_OP_PREFIX_LEN 15 /* == strlen("::tcl::mathop::") */ +    memcpy(mathFuncName, "::tcl::mathop::", MATH_OP_PREFIX_LEN); +    for (opcmdInfoPtr=mathOpCmds ; opcmdInfoPtr->name!=NULL ; opcmdInfoPtr++){ +	TclOpCmdClientData *occdPtr = ckalloc(sizeof(TclOpCmdClientData)); + +	occdPtr->op = opcmdInfoPtr->name; +	occdPtr->i.numArgs = opcmdInfoPtr->i.numArgs; +	occdPtr->expected = opcmdInfoPtr->expected; +	strcpy(mathFuncName + MATH_OP_PREFIX_LEN, opcmdInfoPtr->name); +	cmdPtr = (Command *) Tcl_CreateObjCommand(interp, mathFuncName, +		opcmdInfoPtr->objProc, occdPtr, DeleteOpCmdClientData); +	if (cmdPtr == NULL) { +	    Tcl_Panic("failed to create math operator %s", +		    opcmdInfoPtr->name); +	} else if (opcmdInfoPtr->compileProc != NULL) { +	    cmdPtr->compileProc = opcmdInfoPtr->compileProc;  	} -	mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); -	mathFuncPtr->builtinFuncIndex = i; -	i++;      } -    iPtr->flags |= EXPR_INITIALIZED;      /*       * Do Multiple/Safe Interps Tcl init stuff       */      TclInterpInit(interp); +    TclSetupEnv(interp);      /* -     * We used to create the "errorInfo" and "errorCode" global vars at this -     * point because so much of the Tcl implementation assumes they already -     * exist. This is not quite enough, however, since they can be unset -     * at any time. -     * -     * There are 2 choices: -     *    + Check every place where a GetVar of those is used  -     *      and the NULL result is not checked (like in tclLoad.c) -     *    + Make SetVar,... NULL friendly -     * We choose the second option because : -     *    + It is easy and low cost to check for NULL pointer before -     *      calling strlen() -     *    + It can be helpfull to other people using those API -     *    + Passing a NULL value to those closest 'meaning' is empty string -     *      (specially with the new objects where 0 bytes strings are ok) -     * So the following init is commented out:              -- dl -     * -     * (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorInfo", (char *) NULL, -     *       "", TCL_GLOBAL_ONLY); -     * (void) Tcl_SetVar2((Tcl_Interp *)iPtr, "errorCode", (char *) NULL, -     *       "NONE", TCL_GLOBAL_ONLY); +     * TIP #59: Make embedded configuration information available.       */ -#ifndef TCL_GENERIC_ONLY -    TclSetupEnv(interp); -#endif +    TclInitEmbeddedConfigurationInformation(interp);      /*       * Compute the byte order of this machine. @@ -594,62 +932,77 @@ Tcl_CreateInterp()      Tcl_SetVar2Ex(interp, "tcl_platform", "wordSize",  	    Tcl_NewLongObj((long) sizeof(long)), TCL_GLOBAL_ONLY); +    /* TIP #291 */ +    Tcl_SetVar2Ex(interp, "tcl_platform", "pointerSize", +	    Tcl_NewLongObj((long) sizeof(void *)), TCL_GLOBAL_ONLY); +      /*       * Set up other variables such as tcl_version and tcl_library       */      Tcl_SetVar(interp, "tcl_patchLevel", TCL_PATCH_LEVEL, TCL_GLOBAL_ONLY);      Tcl_SetVar(interp, "tcl_version", TCL_VERSION, TCL_GLOBAL_ONLY); -    Tcl_TraceVar2(interp, "tcl_precision", (char *) NULL, +    Tcl_TraceVar2(interp, "tcl_precision", NULL,  	    TCL_GLOBAL_ONLY|TCL_TRACE_READS|TCL_TRACE_WRITES|TCL_TRACE_UNSETS, -	    TclPrecTraceProc, (ClientData) NULL); +	    TclPrecTraceProc, NULL);      TclpSetVariables(interp);  #ifdef TCL_THREADS      /* -     * The existence of the "threaded" element of the tcl_platform array indicates -     * that this particular Tcl shell has been compiled with threads turned on. -     * Using "info exists tcl_platform(threaded)" a Tcl script can introspect on the  -     * interpreter level of thread safety. +     * The existence of the "threaded" element of the tcl_platform array +     * indicates that this particular Tcl shell has been compiled with threads +     * turned on. Using "info exists tcl_platform(threaded)" a Tcl script can +     * introspect on the interpreter level of thread safety.       */ - -    Tcl_SetVar2(interp, "tcl_platform", "threaded", "1", -	    TCL_GLOBAL_ONLY); +    Tcl_SetVar2(interp, "tcl_platform", "threaded", "1", TCL_GLOBAL_ONLY);  #endif      /*       * Register Tcl's version number. -     * TIP#268: Expose information about its status, -     *          for runtime switches in the core library -     *          and tests. +     * TIP #268: Full patchlevel instead of just major.minor       */ -    Tcl_PkgProvideEx(interp, "Tcl", TCL_VERSION, (ClientData) &tclStubs); +    Tcl_PkgProvideEx(interp, "Tcl", TCL_PATCH_LEVEL, &tclStubs); -#ifdef TCL_TIP268 -    Tcl_SetVar2(interp, "tcl_platform", "tip,268", "1", -	    TCL_GLOBAL_ONLY); -#endif -#ifdef TCL_TIP280 -    Tcl_SetVar2(interp, "tcl_platform", "tip,280", "1", -	    TCL_GLOBAL_ONLY); -#endif -#ifdef Tcl_InitStubs -#undef Tcl_InitStubs +    if (TclTommath_Init(interp) != TCL_OK) { +	Tcl_Panic("%s", Tcl_GetString(Tcl_GetObjResult(interp))); +    } + +    if (TclOOInit(interp) != TCL_OK) { +	Tcl_Panic("%s", Tcl_GetString(Tcl_GetObjResult(interp))); +    } + +    /* +     * Only build in zlib support if we've successfully detected a library to +     * compile and link against. +     */ + +#ifdef HAVE_ZLIB +    if (TclZlibInit(interp) != TCL_OK) { +	Tcl_Panic("%s", Tcl_GetString(Tcl_GetObjResult(interp))); +    }  #endif -    Tcl_InitStubs(interp, TCL_VERSION, 1); +    TOP_CB(iPtr) = NULL;      return interp;  } + +static void +DeleteOpCmdClientData( +    ClientData clientData) +{ +    TclOpCmdClientData *occdPtr = clientData; + +    ckfree(occdPtr); +}  /*   *----------------------------------------------------------------------   *   * TclHideUnsafeCommands --   * - *	Hides base commands that are not marked as safe from this - *	interpreter. + *	Hides base commands that are not marked as safe from this interpreter.   *   * Results:   *	TCL_OK if it succeeds, TCL_ERROR else. @@ -661,19 +1014,20 @@ Tcl_CreateInterp()   */  int -TclHideUnsafeCommands(interp) -    Tcl_Interp *interp;		/* Hide commands in this interpreter. */ +TclHideUnsafeCommands( +    Tcl_Interp *interp)		/* Hide commands in this interpreter. */  { -    register CmdInfo *cmdInfoPtr; +    register const CmdInfo *cmdInfoPtr; -    if (interp == (Tcl_Interp *) NULL) { -        return TCL_ERROR; +    if (interp == NULL) { +	return TCL_ERROR;      }      for (cmdInfoPtr = builtInCmds; cmdInfoPtr->name != NULL; cmdInfoPtr++) { -        if (!cmdInfoPtr->isSafe) { -            Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); -        } +	if (!(cmdInfoPtr->flags & CMD_IS_SAFE)) { +	    Tcl_HideCommand(interp, cmdInfoPtr->name, cmdInfoPtr->name); +	}      } +    TclMakeFileCommandSafe(interp);     /* Ugh! */      return TCL_OK;  } @@ -682,48 +1036,46 @@ TclHideUnsafeCommands(interp)   *   * Tcl_CallWhenDeleted --   * - *	Arrange for a procedure to be called before a given - *	interpreter is deleted. The procedure is called as soon - *	as Tcl_DeleteInterp is called; if Tcl_CallWhenDeleted is - *	called on an interpreter that has already been deleted, - *	the procedure will be called when the last Tcl_Release is + *	Arrange for a function to be called before a given interpreter is + *	deleted. The function is called as soon as Tcl_DeleteInterp is called; + *	if Tcl_CallWhenDeleted is called on an interpreter that has already + *	been deleted, the function will be called when the last Tcl_Release is   *	done on the interpreter.   *   * Results:   *	None.   *   * Side effects: - *	When Tcl_DeleteInterp is invoked to delete interp, - *	proc will be invoked.  See the manual entry for - *	details. + *	When Tcl_DeleteInterp is invoked to delete interp, proc will be + *	invoked. See the manual entry for details.   *   *--------------------------------------------------------------   */  void -Tcl_CallWhenDeleted(interp, proc, clientData) -    Tcl_Interp *interp;		/* Interpreter to watch. */ -    Tcl_InterpDeleteProc *proc;	/* Procedure to call when interpreter -				 * is about to be deleted. */ -    ClientData clientData;	/* One-word value to pass to proc. */ +Tcl_CallWhenDeleted( +    Tcl_Interp *interp,		/* Interpreter to watch. */ +    Tcl_InterpDeleteProc *proc,	/* Function to call when interpreter is about +				 * to be deleted. */ +    ClientData clientData)	/* One-word value to pass to proc. */  {      Interp *iPtr = (Interp *) interp;      static Tcl_ThreadDataKey assocDataCounterKey;      int *assocDataCounterPtr =  	    Tcl_GetThreadData(&assocDataCounterKey, (int)sizeof(int)); -    int new; +    int isNew;      char buffer[32 + TCL_INTEGER_SPACE]; -    AssocData *dPtr = (AssocData *) ckalloc(sizeof(AssocData)); +    AssocData *dPtr = ckalloc(sizeof(AssocData));      Tcl_HashEntry *hPtr;      sprintf(buffer, "Assoc Data Key #%d", *assocDataCounterPtr);      (*assocDataCounterPtr)++; -    if (iPtr->assocData == (Tcl_HashTable *) NULL) { -        iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); -        Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); +    if (iPtr->assocData == NULL) { +	iPtr->assocData = ckalloc(sizeof(Tcl_HashTable)); +	Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS);      } -    hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &new); +    hPtr = Tcl_CreateHashEntry(iPtr->assocData, buffer, &isNew);      dPtr->proc = proc;      dPtr->clientData = clientData;      Tcl_SetHashValue(hPtr, dPtr); @@ -734,27 +1086,26 @@ Tcl_CallWhenDeleted(interp, proc, clientData)   *   * Tcl_DontCallWhenDeleted --   * - *	Cancel the arrangement for a procedure to be called when - *	a given interpreter is deleted. + *	Cancel the arrangement for a function to be called when a given + *	interpreter is deleted.   *   * Results:   *	None.   *   * Side effects: - *	If proc and clientData were previously registered as a - *	callback via Tcl_CallWhenDeleted, they are unregistered. - *	If they weren't previously registered then nothing - *	happens. + *	If proc and clientData were previously registered as a callback via + *	Tcl_CallWhenDeleted, they are unregistered. If they weren't previously + *	registered then nothing happens.   *   *--------------------------------------------------------------   */  void -Tcl_DontCallWhenDeleted(interp, proc, clientData) -    Tcl_Interp *interp;		/* Interpreter to watch. */ -    Tcl_InterpDeleteProc *proc;	/* Procedure to call when interpreter -				 * is about to be deleted. */ -    ClientData clientData;	/* One-word value to pass to proc. */ +Tcl_DontCallWhenDeleted( +    Tcl_Interp *interp,		/* Interpreter to watch. */ +    Tcl_InterpDeleteProc *proc,	/* Function to call when interpreter is about +				 * to be deleted. */ +    ClientData clientData)	/* One-word value to pass to proc. */  {      Interp *iPtr = (Interp *) interp;      Tcl_HashTable *hTablePtr; @@ -763,17 +1114,17 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData)      AssocData *dPtr;      hTablePtr = iPtr->assocData; -    if (hTablePtr == (Tcl_HashTable *) NULL) { -        return; +    if (hTablePtr == NULL) { +	return;      }      for (hPtr = Tcl_FirstHashEntry(hTablePtr, &hSearch); hPtr != NULL;  	    hPtr = Tcl_NextHashEntry(&hSearch)) { -        dPtr = (AssocData *) Tcl_GetHashValue(hPtr); -        if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { -            ckfree((char *) dPtr); -            Tcl_DeleteHashEntry(hPtr); -            return; -        } +	dPtr = Tcl_GetHashValue(hPtr); +	if ((dPtr->proc == proc) && (dPtr->clientData == clientData)) { +	    ckfree(dPtr); +	    Tcl_DeleteHashEntry(hPtr); +	    return; +	}      }  } @@ -783,9 +1134,9 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData)   * Tcl_SetAssocData --   *   *	Creates a named association between user-specified data, a delete - *	function and this interpreter. If the association already exists - *	the data is overwritten with the new data. The delete function will - *	be invoked when the interpreter is deleted. + *	function and this interpreter. If the association already exists the + *	data is overwritten with the new data. The delete function will be + *	invoked when the interpreter is deleted.   *   * Results:   *	None. @@ -797,27 +1148,27 @@ Tcl_DontCallWhenDeleted(interp, proc, clientData)   */  void -Tcl_SetAssocData(interp, name, proc, clientData) -    Tcl_Interp *interp;		/* Interpreter to associate with. */ -    CONST char *name;		/* Name for association. */ -    Tcl_InterpDeleteProc *proc;	/* Proc to call when interpreter is -                                 * about to be deleted. */ -    ClientData clientData;	/* One-word value to pass to proc. */ +Tcl_SetAssocData( +    Tcl_Interp *interp,		/* Interpreter to associate with. */ +    const char *name,		/* Name for association. */ +    Tcl_InterpDeleteProc *proc,	/* Proc to call when interpreter is about to +				 * be deleted. */ +    ClientData clientData)	/* One-word value to pass to proc. */  {      Interp *iPtr = (Interp *) interp;      AssocData *dPtr;      Tcl_HashEntry *hPtr; -    int new; +    int isNew; -    if (iPtr->assocData == (Tcl_HashTable *) NULL) { -        iPtr->assocData = (Tcl_HashTable *) ckalloc(sizeof(Tcl_HashTable)); -        Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS); +    if (iPtr->assocData == NULL) { +	iPtr->assocData = ckalloc(sizeof(Tcl_HashTable)); +	Tcl_InitHashTable(iPtr->assocData, TCL_STRING_KEYS);      } -    hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &new); -    if (new == 0) { -        dPtr = (AssocData *) Tcl_GetHashValue(hPtr); +    hPtr = Tcl_CreateHashEntry(iPtr->assocData, name, &isNew); +    if (isNew == 0) { +	dPtr = Tcl_GetHashValue(hPtr);      } else { -        dPtr = (AssocData *) ckalloc(sizeof(AssocData)); +	dPtr = ckalloc(sizeof(AssocData));      }      dPtr->proc = proc;      dPtr->clientData = clientData; @@ -830,8 +1181,8 @@ Tcl_SetAssocData(interp, name, proc, clientData)   *   * Tcl_DeleteAssocData --   * - *	Deletes a named association of user-specified data with - *	the specified interpreter. + *	Deletes a named association of user-specified data with the specified + *	interpreter.   *   * Results:   *	None. @@ -843,26 +1194,26 @@ Tcl_SetAssocData(interp, name, proc, clientData)   */  void -Tcl_DeleteAssocData(interp, name) -    Tcl_Interp *interp;			/* Interpreter to associate with. */ -    CONST char *name;			/* Name of association. */ +Tcl_DeleteAssocData( +    Tcl_Interp *interp,		/* Interpreter to associate with. */ +    const char *name)		/* Name of association. */  {      Interp *iPtr = (Interp *) interp;      AssocData *dPtr;      Tcl_HashEntry *hPtr; -    if (iPtr->assocData == (Tcl_HashTable *) NULL) { -        return; +    if (iPtr->assocData == NULL) { +	return;      }      hPtr = Tcl_FindHashEntry(iPtr->assocData, name); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        return; +    if (hPtr == NULL) { +	return;      } -    dPtr = (AssocData *) Tcl_GetHashValue(hPtr); +    dPtr = Tcl_GetHashValue(hPtr);      if (dPtr->proc != NULL) { -        (dPtr->proc) (dPtr->clientData, interp); +	dPtr->proc(dPtr->clientData, interp);      } -    ckfree((char *) dPtr); +    ckfree(dPtr);      Tcl_DeleteHashEntry(hPtr);  } @@ -871,8 +1222,8 @@ Tcl_DeleteAssocData(interp, name)   *   * Tcl_GetAssocData --   * - *	Returns the client data associated with this name in the - *	specified interpreter. + *	Returns the client data associated with this name in the specified + *	interpreter.   *   * Results:   *	The client data in the AssocData record denoted by the named @@ -885,26 +1236,27 @@ Tcl_DeleteAssocData(interp, name)   */  ClientData -Tcl_GetAssocData(interp, name, procPtr) -    Tcl_Interp *interp;			/* Interpreter associated with. */ -    CONST char *name;			/* Name of association. */ -    Tcl_InterpDeleteProc **procPtr;	/* Pointer to place to store address -					 * of current deletion callback. */ +Tcl_GetAssocData( +    Tcl_Interp *interp,		/* Interpreter associated with. */ +    const char *name,		/* Name of association. */ +    Tcl_InterpDeleteProc **procPtr) +				/* Pointer to place to store address of +				 * current deletion callback. */  {      Interp *iPtr = (Interp *) interp;      AssocData *dPtr;      Tcl_HashEntry *hPtr; -    if (iPtr->assocData == (Tcl_HashTable *) NULL) { -        return (ClientData) NULL; +    if (iPtr->assocData == NULL) { +	return NULL;      }      hPtr = Tcl_FindHashEntry(iPtr->assocData, name); -    if (hPtr == (Tcl_HashEntry *) NULL) { -        return (ClientData) NULL; +    if (hPtr == NULL) { +	return NULL;      } -    dPtr = (AssocData *) Tcl_GetHashValue(hPtr); -    if (procPtr != (Tcl_InterpDeleteProc **) NULL) { -        *procPtr = dPtr->proc; +    dPtr = Tcl_GetHashValue(hPtr); +    if (procPtr != NULL) { +	*procPtr = dPtr->proc;      }      return dPtr->clientData;  } @@ -914,8 +1266,8 @@ Tcl_GetAssocData(interp, name, procPtr)   *   * Tcl_InterpDeleted --   * - *	Returns nonzero if the interpreter has been deleted with a call - *	to Tcl_DeleteInterp. + *	Returns nonzero if the interpreter has been deleted with a call to + *	Tcl_DeleteInterp.   *   * Results:   *	Nonzero if the interpreter is deleted, zero otherwise. @@ -927,8 +1279,8 @@ Tcl_GetAssocData(interp, name, procPtr)   */  int -Tcl_InterpDeleted(interp) -    Tcl_Interp *interp; +Tcl_InterpDeleted( +    Tcl_Interp *interp)  {      return (((Interp *) interp)->flags & DELETED) ? 1 : 0;  } @@ -938,11 +1290,11 @@ Tcl_InterpDeleted(interp)   *   * Tcl_DeleteInterp --   * - *	Ensures that the interpreter will be deleted eventually. If there - *	are no Tcl_Preserve calls in effect for this interpreter, it is - *	deleted immediately, otherwise the interpreter is deleted when - *	the last Tcl_Preserve is matched by a call to Tcl_Release. In either - *	case, the procedure runs the currently registered deletion callbacks.  + *	Ensures that the interpreter will be deleted eventually. If there are + *	no Tcl_Preserve calls in effect for this interpreter, it is deleted + *	immediately, otherwise the interpreter is deleted when the last + *	Tcl_Preserve is matched by a call to Tcl_Release. In either case, the + *	function runs the currently registered deletion callbacks.   *   * Results:   *	None. @@ -957,9 +1309,9 @@ Tcl_InterpDeleted(interp)   */  void -Tcl_DeleteInterp(interp) -    Tcl_Interp *interp;		/* Token for command interpreter (returned -				 * by a previous call to Tcl_CreateInterp). */ +Tcl_DeleteInterp( +    Tcl_Interp *interp)		/* Token for command interpreter (returned by +				 * a previous call to Tcl_CreateInterp). */  {      Interp *iPtr = (Interp *) interp; @@ -968,21 +1320,22 @@ Tcl_DeleteInterp(interp)       */      if (iPtr->flags & DELETED) { -        return; +	return;      } -     +      /*       * Mark the interpreter as deleted. No further evals will be allowed. +     * Increase the compileEpoch as a signal to compiled bytecodes.       */      iPtr->flags |= DELETED; +    iPtr->compileEpoch++;      /*       * Ensure that the interpreter is eventually deleted.       */ -    Tcl_EventuallyFree((ClientData) interp, -            (Tcl_FreeProc *) DeleteInterpProc); +    Tcl_EventuallyFree(interp, (Tcl_FreeProc *) DeleteInterpProc);  }  /* @@ -990,153 +1343,212 @@ Tcl_DeleteInterp(interp)   *   * DeleteInterpProc --   * - *	Helper procedure to delete an interpreter. This procedure is - *	called when the last call to Tcl_Preserve on this interpreter - *	is matched by a call to Tcl_Release. The procedure cleans up - *	all resources used in the interpreter and calls all currently - *	registered interpreter deletion callbacks. + *	Helper function to delete an interpreter. This function is called when + *	the last call to Tcl_Preserve on this interpreter is matched by a call + *	to Tcl_Release. The function cleans up all resources used in the + *	interpreter and calls all currently registered interpreter deletion + *	callbacks.   *   * Results:   *	None.   *   * Side effects: - *	Whatever the interpreter deletion callbacks do. Frees resources - *	used by the interpreter. + *	Whatever the interpreter deletion callbacks do. Frees resources used + *	by the interpreter.   *   *----------------------------------------------------------------------   */  static void -DeleteInterpProc(interp) -    Tcl_Interp *interp;			/* Interpreter to delete. */ +DeleteInterpProc( +    Tcl_Interp *interp)		/* Interpreter to delete. */  {      Interp *iPtr = (Interp *) interp;      Tcl_HashEntry *hPtr;      Tcl_HashSearch search;      Tcl_HashTable *hTablePtr;      ResolverScheme *resPtr, *nextResPtr; +    int i;      /* -     * Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup. +     * Punt if there is an error in the Tcl_Release/Tcl_Preserve matchup, +	 * unless we are exiting.       */ -     -    if (iPtr->numLevels > 0) { -        panic("DeleteInterpProc called with active evals"); + +    if ((iPtr->numLevels > 0) && !TclInExit()) { +	Tcl_Panic("DeleteInterpProc called with active evals");      }      /* -     * The interpreter should already be marked deleted; otherwise how -     * did we get here? +     * The interpreter should already be marked deleted; otherwise how did we +     * get here?       */      if (!(iPtr->flags & DELETED)) { -        panic("DeleteInterpProc called on interpreter not marked deleted"); +	Tcl_Panic("DeleteInterpProc called on interpreter not marked deleted");      } -    TclHandleFree(iPtr->handle); +    /* +     * TIP #219, Tcl Channel Reflection API. Discard a leftover state. +     */ + +    if (iPtr->chanMsg != NULL) { +	Tcl_DecrRefCount(iPtr->chanMsg); +	iPtr->chanMsg = NULL; +    } + +    /* +     * TIP #285, Script cancellation support. Delete this interp from the +     * global hash table of CancelInfo structs. +     */ + +    Tcl_MutexLock(&cancelLock); +    hPtr = Tcl_FindHashEntry(&cancelTable, (char *) iPtr); +    if (hPtr != NULL) { +	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. +     */ + +    TclRemoveScriptLimitCallbacks(interp); +    TclLimitRemoveAllHandlers(interp);      /* -     * Dismantle everything in the global namespace except for the -     * "errorInfo" and "errorCode" variables. These remain until the -     * namespace is actually destroyed, in case any errors occur. -     *          * Dismantle the namespace here, before we clear the assocData. If any       * background errors occur here, they will be deleted below. +     * +     * Dismantle the namespace after freeing the iPtr->handle so that each +     * bytecode releases its literals without caring to update the literal +     * table, as it will be freed later in this function without further use.       */ -     + +    TclHandleFree(iPtr->handle);      TclTeardownNamespace(iPtr->globalNsPtr);      /*       * Delete all the hidden commands.       */ -      +      hTablePtr = iPtr->hiddenCmdTablePtr;      if (hTablePtr != NULL) {  	/* -	 * Non-pernicious deletion.  The deletion callbacks will not be -	 * allowed to create any new hidden or non-hidden commands. -	 * Tcl_DeleteCommandFromToken() will remove the entry from the +	 * Non-pernicious deletion. The deletion callbacks will not be allowed +	 * to create any new hidden or non-hidden commands. +	 * Tcl_DeleteCommandFromToken will remove the entry from the  	 * hiddenCmdTablePtr.  	 */ -	  +  	hPtr = Tcl_FirstHashEntry(hTablePtr, &search); -	for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { -	    Tcl_DeleteCommandFromToken(interp, -		    (Tcl_Command) Tcl_GetHashValue(hPtr)); +	for (; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) { +	    Tcl_DeleteCommandFromToken(interp, Tcl_GetHashValue(hPtr));  	}  	Tcl_DeleteHashTable(hTablePtr); -	ckfree((char *) hTablePtr); +	ckfree(hTablePtr);      } -    /* -     * Tear down the math function table. -     */ - -    for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &search); -	     hPtr != NULL; -             hPtr = Tcl_NextHashEntry(&search)) { -	ckfree((char *) Tcl_GetHashValue(hPtr)); -    } -    Tcl_DeleteHashTable(&iPtr->mathFuncTable);      /*       * Invoke deletion callbacks; note that a callback can create new       * callbacks, so we iterate.       */ -    while (iPtr->assocData != (Tcl_HashTable *) NULL) { +    while (iPtr->assocData != NULL) {  	AssocData *dPtr; -	 -        hTablePtr = iPtr->assocData; -        iPtr->assocData = (Tcl_HashTable *) NULL; -        for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); -                 hPtr != NULL; -                 hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { -            dPtr = (AssocData *) Tcl_GetHashValue(hPtr); -            Tcl_DeleteHashEntry(hPtr); -            if (dPtr->proc != NULL) { -                (*dPtr->proc)(dPtr->clientData, interp); -            } -            ckfree((char *) dPtr); -        } -        Tcl_DeleteHashTable(hTablePtr); -        ckfree((char *) hTablePtr); + +	hTablePtr = iPtr->assocData; +	iPtr->assocData = NULL; +	for (hPtr = Tcl_FirstHashEntry(hTablePtr, &search); +		hPtr != NULL; +		hPtr = Tcl_FirstHashEntry(hTablePtr, &search)) { +	    dPtr = Tcl_GetHashValue(hPtr); +	    Tcl_DeleteHashEntry(hPtr); +	    if (dPtr->proc != NULL) { +		dPtr->proc(dPtr->clientData, interp); +	    } +	    ckfree(dPtr); +	} +	Tcl_DeleteHashTable(hTablePtr); +	ckfree(hTablePtr);      }      /* -     * Finish deleting the global namespace. +     * Pop the root frame pointer and finish deleting the global +     * namespace. The order is important [Bug 1658572].       */ -     + +    if ((iPtr->framePtr != iPtr->rootFramePtr) && !TclInExit()) { +	Tcl_Panic("DeleteInterpProc: popping rootCallFrame with other frames on top"); +    } +    Tcl_PopCallFrame(interp); +    ckfree(iPtr->rootFramePtr); +    iPtr->rootFramePtr = NULL;      Tcl_DeleteNamespace((Tcl_Namespace *) iPtr->globalNsPtr);      /* -     * Free up the result *after* deleting variables, since variable -     * deletion could have transferred ownership of the result string -     * to Tcl. +     * Free up the result *after* deleting variables, since variable deletion +     * could have transferred ownership of the result string to Tcl.       */      Tcl_FreeResult(interp); -    interp->result = NULL; +    iPtr->result = NULL;      Tcl_DecrRefCount(iPtr->objResultPtr);      iPtr->objResultPtr = NULL; -    if (iPtr->errorInfo != NULL) { -	ckfree(iPtr->errorInfo); -        iPtr->errorInfo = NULL; -    } -    if (iPtr->errorCode != NULL) { -	ckfree(iPtr->errorCode); -        iPtr->errorCode = NULL; +    Tcl_DecrRefCount(iPtr->ecVar); +    if (iPtr->errorCode) { +	Tcl_DecrRefCount(iPtr->errorCode); +	iPtr->errorCode = NULL; +    } +    Tcl_DecrRefCount(iPtr->eiVar); +    if (iPtr->errorInfo) { +	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);      }      if (iPtr->appendResult != NULL) {  	ckfree(iPtr->appendResult); -        iPtr->appendResult = NULL; +	iPtr->appendResult = NULL;      }      TclFreePackageInfo(iPtr);      while (iPtr->tracePtr != NULL) { -	Tcl_DeleteTrace((Tcl_Interp*) iPtr, (Tcl_Trace) iPtr->tracePtr); +	Tcl_DeleteTrace((Tcl_Interp *) iPtr, (Tcl_Trace) iPtr->tracePtr);      }      if (iPtr->execEnvPtr != NULL) {  	TclDeleteExecEnv(iPtr->execEnvPtr);      } +    if (iPtr->scriptFile) { +	Tcl_DecrRefCount(iPtr->scriptFile); +	iPtr->scriptFile = NULL; +    }      Tcl_DecrRefCount(iPtr->emptyObjPtr);      iPtr->emptyObjPtr = NULL; @@ -1144,110 +1556,110 @@ DeleteInterpProc(interp)      while (resPtr) {  	nextResPtr = resPtr->nextPtr;  	ckfree(resPtr->name); -	ckfree((char *) resPtr); -        resPtr = nextResPtr; +	ckfree(resPtr); +	resPtr = nextResPtr;      } -     +      /*       * Free up literal objects created for scripts compiled by the       * interpreter.       */ -    TclDeleteLiteralTable(interp, &(iPtr->literalTable)); +    TclDeleteLiteralTable(interp, &iPtr->literalTable); -#ifdef TCL_TIP280 -    /* TIP #280 - Release the arrays for ByteCode/Proc extension, and contents. +    /* +     * TIP #280 - Release the arrays for ByteCode/Proc extension, and +     * contents.       */ -    { -        Tcl_HashEntry *hPtr; -	Tcl_HashSearch hSearch; -	CmdFrame*      cfPtr; -	ExtCmdLoc*     eclPtr; -	int            i; - -	for (hPtr = Tcl_FirstHashEntry(iPtr->linePBodyPtr, &hSearch); -	     hPtr != NULL; -	     hPtr = Tcl_NextHashEntry(&hSearch)) { -	    cfPtr = (CmdFrame*) Tcl_GetHashValue (hPtr); +    for (hPtr = Tcl_FirstHashEntry(iPtr->linePBodyPtr, &search); +	    hPtr != NULL; +	    hPtr = Tcl_NextHashEntry(&search)) { +	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); +		Tcl_DecrRefCount(cfPtr->data.eval.path);  	    } -	    ckfree ((char*) cfPtr->line); -	    ckfree ((char*) cfPtr); -	    Tcl_DeleteHashEntry (hPtr); - +	    ckfree(cfPtr->line); +	    ckfree(cfPtr);  	} -	Tcl_DeleteHashTable (iPtr->linePBodyPtr); -	ckfree ((char*) iPtr->linePBodyPtr); -	iPtr->linePBodyPtr = NULL; - -	/* See also tclCompile.c, TclCleanupByteCode */ +	Tcl_DeleteHashEntry(hPtr); +    } +    Tcl_DeleteHashTable(iPtr->linePBodyPtr); +    ckfree(iPtr->linePBodyPtr); +    iPtr->linePBodyPtr = NULL; -	for (hPtr = Tcl_FirstHashEntry(iPtr->lineBCPtr, &hSearch); -	     hPtr != NULL; -	     hPtr = Tcl_NextHashEntry(&hSearch)) { +    /* +     * See also tclCompile.c, TclCleanupByteCode +     */ -	    eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hPtr); +    for (hPtr = Tcl_FirstHashEntry(iPtr->lineBCPtr, &search); +	    hPtr != NULL; +	    hPtr = Tcl_NextHashEntry(&search)) { +	ExtCmdLoc *eclPtr = Tcl_GetHashValue(hPtr); -	    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->type == TCL_LOCATION_SOURCE) { +	    Tcl_DecrRefCount(eclPtr->path); +	} +	for (i=0; i< eclPtr->nuloc; i++) { +	    ckfree(eclPtr->loc[i].line); +	} -            if (eclPtr->loc != NULL) { -		ckfree ((char*) eclPtr->loc); -	    } +	if (eclPtr->loc != NULL) { +	    ckfree(eclPtr->loc); +	} -            if (eclPtr->eiloc != NULL) { -		ckfree ((char*) eclPtr->eiloc); -	    } +	ckfree(eclPtr); +	Tcl_DeleteHashEntry(hPtr); +    } +    Tcl_DeleteHashTable(iPtr->lineBCPtr); +    ckfree(iPtr->lineBCPtr); +    iPtr->lineBCPtr = NULL; -	    ckfree ((char*) eclPtr); -	    Tcl_DeleteHashEntry (hPtr); -	} -	Tcl_DeleteHashTable (iPtr->lineBCPtr); -	ckfree((char*) iPtr->lineBCPtr); -	iPtr->lineBCPtr = NULL; +    /* +     * 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. +     */ +    if (iPtr->lineLAPtr->numEntries && !TclInExit()) {  	/* -	 * 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. +	 * When the interp goes away we have nothing on the stack, so there +	 * are no arguments, so this table has to be 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_Panic("Argument location tracking table not empty"); +    } -	    Tcl_Panic ("Argument location tracking table not empty"); -	} +    Tcl_DeleteHashTable(iPtr->lineLAPtr); +    ckfree((char *) iPtr->lineLAPtr); +    iPtr->lineLAPtr = NULL; -	Tcl_DeleteHashTable (iPtr->lineLAPtr); -	ckfree((char*) iPtr->lineLAPtr); -	iPtr->lineLAPtr = NULL; +    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. +	 */ -	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_Panic("Argument location tracking table not empty"); +    } -	    Tcl_Panic ("Argument location tracking table not empty"); -	} +    Tcl_DeleteHashTable(iPtr->lineLABCPtr); +    ckfree(iPtr->lineLABCPtr); +    iPtr->lineLABCPtr = NULL; -	Tcl_DeleteHashTable (iPtr->lineLABCPtr); -	ckfree((char*) iPtr->lineLABCPtr); -	iPtr->lineLABCPtr = NULL; -    } -#endif -    ckfree((char *) iPtr); +    /* +     * Squelch the tables of traces on variables and searches over arrays in +     * the in the interpreter. +     */ + +    Tcl_DeleteHashTable(&iPtr->varTraces); +    Tcl_DeleteHashTable(&iPtr->varSearches); + +    ckfree(iPtr);  }  /* @@ -1255,79 +1667,78 @@ DeleteInterpProc(interp)   *   * Tcl_HideCommand --   * - *	Makes a command hidden so that it cannot be invoked from within - *	an interpreter, only from within an ancestor. + *	Makes a command hidden so that it cannot be invoked from within an + *	interpreter, only from within an ancestor.   *   * Results: - *	A standard Tcl result; also leaves a message in the interp's result - *	if an error occurs. + *	A standard Tcl result; also leaves a message in the interp's result if + *	an error occurs.   *   * Side effects: - *	Removes a command from the command table and create an entry - *      into the hidden command table under the specified token name. + *	Removes a command from the command table and create an entry into the + *	hidden command table under the specified token name.   *   *---------------------------------------------------------------------------   */  int -Tcl_HideCommand(interp, cmdName, hiddenCmdToken) -    Tcl_Interp *interp;		/* Interpreter in which to hide command. */ -    CONST char *cmdName;	/* Name of command to hide. */ -    CONST char *hiddenCmdToken;	/* Token name of the to-be-hidden command. */ +Tcl_HideCommand( +    Tcl_Interp *interp,		/* Interpreter in which to hide command. */ +    const char *cmdName,	/* Name of command to hide. */ +    const char *hiddenCmdToken)	/* Token name of the to-be-hidden command. */  {      Interp *iPtr = (Interp *) interp;      Tcl_Command cmd;      Command *cmdPtr;      Tcl_HashTable *hiddenCmdTablePtr;      Tcl_HashEntry *hPtr; -    int new; +    int isNew;      if (iPtr->flags & DELETED) { +	/* +	 * The interpreter is being deleted. Do not create any new structures, +	 * because it is not safe to modify the interpreter. +	 */ -        /* -         * The interpreter is being deleted. Do not create any new -         * structures, because it is not safe to modify the interpreter. -         */ -         -        return TCL_ERROR; +	return TCL_ERROR;      }      /*       * Disallow hiding of commands that are currently in a namespace or -     * renaming (as part of hiding) into a namespace. +     * renaming (as part of hiding) into a namespace (because the current +     * implementation with a single global table and the needed uniqueness of +     * names cause problems with namespaces).       * -     * (because the current implementation with a single global table -     *  and the needed uniqueness of names cause problems with namespaces) +     * We don't need to check for "::" in cmdName because the real check is on +     * the nsPtr below.       * -     * we don't need to check for "::" in cmdName because the real check is -     * on the nsPtr below. -     * -     * hiddenCmdToken is just a string which is not interpreted in any way. -     * It may contain :: but the string is not interpreted as a namespace +     * hiddenCmdToken is just a string which is not interpreted in any way. It +     * may contain :: but the string is not interpreted as a namespace       * qualifier command name. Thus, hiding foo::bar to foo::bar and then       * trying to expose or invoke ::foo::bar will NOT work; but if the       * application always uses the same strings it will get consistent       * behaviour.       * -     * But as we currently limit ourselves to the global namespace only -     * for the source, in order to avoid potential confusion, -     * lets prevent "::" in the token too.  --dl +     * But as we currently limit ourselves to the global namespace only for +     * the source, in order to avoid potential confusion, lets prevent "::" in +     * the token too. - dl       */      if (strstr(hiddenCmdToken, "::") != NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "cannot use namespace qualifiers in hidden command", -		" token (rename)", (char *) NULL); -        return TCL_ERROR; +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"cannot use namespace qualifiers in hidden command" +		" token (rename)", -1)); +        Tcl_SetErrorCode(interp, "TCL", "VALUE", "HIDDENTOKEN", NULL); +	return TCL_ERROR;      }      /* -     * Find the command to hide. An error is returned if cmdName can't -     * be found. Look up the command only from the global namespace. -     * Full path of the command must be given if using namespaces. +     * Find the command to hide. An error is returned if cmdName can't be +     * found. Look up the command only from the global namespace. Full path of +     * the command must be given if using namespaces.       */ -    cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, +    cmd = Tcl_FindCommand(interp, cmdName, NULL,  	    /*flags*/ TCL_LEAVE_ERR_MSG | TCL_GLOBAL_ONLY);      if (cmd == (Tcl_Command) NULL) {  	return TCL_ERROR; @@ -1338,22 +1749,22 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken)       * Check that the command is really in global namespace       */ -    if ( cmdPtr->nsPtr != iPtr->globalNsPtr ) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "can only hide global namespace commands", -		" (use rename then hide)", (char *) NULL); -        return TCL_ERROR; +    if (cmdPtr->nsPtr != iPtr->globalNsPtr) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +                "can only hide global namespace commands (use rename then hide)", +                -1)); +        Tcl_SetErrorCode(interp, "TCL", "HIDE", "NON_GLOBAL", NULL); +	return TCL_ERROR;      } -     +      /*       * Initialize the hidden command table if necessary.       */      hiddenCmdTablePtr = iPtr->hiddenCmdTablePtr;      if (hiddenCmdTablePtr == NULL) { -        hiddenCmdTablePtr = (Tcl_HashTable *) -	        ckalloc((unsigned) sizeof(Tcl_HashTable)); -        Tcl_InitHashTable(hiddenCmdTablePtr, TCL_STRING_KEYS); +	hiddenCmdTablePtr = ckalloc(sizeof(Tcl_HashTable)); +	Tcl_InitHashTable(hiddenCmdTablePtr, TCL_STRING_KEYS);  	iPtr->hiddenCmdTablePtr = hiddenCmdTablePtr;      } @@ -1362,20 +1773,20 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken)       * hiddenCmdToken if a hidden command with the name hiddenCmdToken already       * exists.       */ -     -    hPtr = Tcl_CreateHashEntry(hiddenCmdTablePtr, hiddenCmdToken, &new); -    if (!new) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "hidden command named \"", hiddenCmdToken, "\" already exists", -                (char *) NULL); -        return TCL_ERROR; + +    hPtr = Tcl_CreateHashEntry(hiddenCmdTablePtr, hiddenCmdToken, &isNew); +    if (!isNew) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "hidden command named \"%s\" already exists", +                hiddenCmdToken)); +        Tcl_SetErrorCode(interp, "TCL", "HIDE", "ALREADY_HIDDEN", NULL); +	return TCL_ERROR;      }      /* -     * Nb : This code is currently 'like' a rename to a specialy set apart -     * name table. Changes here and in TclRenameCommand must -     * be kept in synch untill the common parts are actually -     * factorized out. +     * NB: This code is currently 'like' a rename to a specialy set apart name +     * table. Changes here and in TclRenameCommand must be kept in synch until +     * the common parts are actually factorized out.       */      /* @@ -1385,26 +1796,34 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken)       */      if (cmdPtr->hPtr != NULL) { -        Tcl_DeleteHashEntry(cmdPtr->hPtr); -        cmdPtr->hPtr = (Tcl_HashEntry *) NULL; +	Tcl_DeleteHashEntry(cmdPtr->hPtr); +	cmdPtr->hPtr = NULL;  	cmdPtr->cmdEpoch++;      }      /* -     * Now link the hash table entry with the command structure. -     * We ensured above that the nsPtr was right. +     * The list of command exported from the namespace might have changed. +     * However, we do not need to recompute this just yet; next time we need +     * the info will be soon enough.       */ -     + +    TclInvalidateNsCmdLookup(cmdPtr->nsPtr); + +    /* +     * Now link the hash table entry with the command structure. We ensured +     * above that the nsPtr was right. +     */ +      cmdPtr->hPtr = hPtr; -    Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); +    Tcl_SetHashValue(hPtr, cmdPtr);      /* -     * If the command being hidden has a compile procedure, increment the -     * interpreter's compileEpoch to invalidate its compiled code. This -     * makes sure that we don't later try to execute old code compiled with -     * command-specific (i.e., inline) bytecodes for the now-hidden -     * command. This field is checked in Tcl_EvalObj and ObjInterpProc, -     * and code whose compilation epoch doesn't match is recompiled. +     * If the command being hidden has a compile function, increment the +     * interpreter's compileEpoch to invalidate its compiled code. This makes +     * sure that we don't later try to execute old code compiled with +     * command-specific (i.e., inline) bytecodes for the now-hidden command. +     * This field is checked in Tcl_EvalObj and ObjInterpProc, and code whose +     * compilation epoch doesn't match is recompiled.       */      if (cmdPtr->compileProc != NULL) { @@ -1418,12 +1837,12 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken)   *   * Tcl_ExposeCommand --   * - *	Makes a previously hidden command callable from inside the - *	interpreter instead of only by its ancestors. + *	Makes a previously hidden command callable from inside the interpreter + *	instead of only by its ancestors.   *   * Results: - *	A standard Tcl result. If an error occurs, a message is left - *	in the interp's result. + *	A standard Tcl result. If an error occurs, a message is left in the + *	interp's result.   *   * Side effects:   *	Moves commands from one hash table to another. @@ -1432,40 +1851,40 @@ Tcl_HideCommand(interp, cmdName, hiddenCmdToken)   */  int -Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName) -    Tcl_Interp *interp;		/* Interpreter in which to make command -                                 * callable. */ -    CONST char *hiddenCmdToken;	/* Name of hidden command. */ -    CONST char *cmdName;	/* Name of to-be-exposed command. */ +Tcl_ExposeCommand( +    Tcl_Interp *interp,		/* Interpreter in which to make command +				 * callable. */ +    const char *hiddenCmdToken,	/* Name of hidden command. */ +    const char *cmdName)	/* Name of to-be-exposed command. */  {      Interp *iPtr = (Interp *) interp;      Command *cmdPtr;      Namespace *nsPtr;      Tcl_HashEntry *hPtr;      Tcl_HashTable *hiddenCmdTablePtr; -    int new; +    int isNew;      if (iPtr->flags & DELETED) { -        /* -         * The interpreter is being deleted. Do not create any new -         * structures, because it is not safe to modify the interpreter. -         */ -         -        return TCL_ERROR; +	/* +	 * The interpreter is being deleted. Do not create any new structures, +	 * because it is not safe to modify the interpreter. +	 */ + +	return TCL_ERROR;      }      /* -     * Check that we have a regular name for the command -     * (that the user is not trying to do an expose and a rename -     *  (to another namespace) at the same time) +     * Check that we have a regular name for the command (that the user is not +     * trying to do an expose and a rename (to another namespace) at the same +     * time).       */      if (strstr(cmdName, "::") != NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "can not expose to a namespace ", -		"(use expose to toplevel, then rename)", -                 (char *) NULL); -        return TCL_ERROR; +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +                "cannot expose to a namespace (use expose to toplevel, then rename)", +                -1)); +        Tcl_SetErrorCode(interp, "TCL", "EXPOSE", "NON_GLOBAL", NULL); +	return TCL_ERROR;      }      /* @@ -1477,82 +1896,104 @@ Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName)      if (hiddenCmdTablePtr != NULL) {  	hPtr = Tcl_FindHashEntry(hiddenCmdTablePtr, hiddenCmdToken);      } -    if (hPtr == (Tcl_HashEntry *) NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "unknown hidden command \"", hiddenCmdToken, -                "\"", (char *) NULL); -        return TCL_ERROR; +    if (hPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "unknown hidden command \"%s\"", hiddenCmdToken)); +        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "HIDDENTOKEN", +                hiddenCmdToken, NULL); +	return TCL_ERROR;      } -    cmdPtr = (Command *) Tcl_GetHashValue(hPtr); -     +    cmdPtr = Tcl_GetHashValue(hPtr);      /* -     * Check that we have a true global namespace -     * command (enforced by Tcl_HideCommand() but let's double -     * check. (If it was not, we would not really know how to -     * handle it). +     * Check that we have a true global namespace command (enforced by +     * Tcl_HideCommand but let's double check. (If it was not, we would not +     * really know how to handle it).       */ -    if ( cmdPtr->nsPtr != iPtr->globalNsPtr ) { -	/*  -	 * This case is theoritically impossible, -	 * we might rather panic() than 'nicely' erroring out ? + +    if (cmdPtr->nsPtr != iPtr->globalNsPtr) { +	/* +	 * This case is theoritically impossible, we might rather Tcl_Panic +	 * than 'nicely' erroring out ?  	 */ -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "trying to expose a non global command name space command", -		(char *) NULL); -        return TCL_ERROR; + +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"trying to expose a non-global command namespace command", +		-1)); +	return TCL_ERROR;      } -     -    /* This is the global table */ + +    /* +     * This is the global table. +     */ +      nsPtr = cmdPtr->nsPtr;      /* -     * It is an error to overwrite an existing exposed command as a result -     * of exposing a previously hidden command. +     * It is an error to overwrite an existing exposed command as a result of +     * exposing a previously hidden command.       */ -    hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &new); -    if (!new) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "exposed command \"", cmdName, -                "\" already exists", (char *) NULL); -        return TCL_ERROR; +    hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, cmdName, &isNew); +    if (!isNew) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "exposed command \"%s\" already exists", cmdName)); +        Tcl_SetErrorCode(interp, "TCL", "EXPOSE", "COMMAND_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. +     */ + +    TclInvalidateNsCmdLookup(nsPtr); + +    /*       * Remove the hash entry for the command from the interpreter hidden       * command table.       */      if (cmdPtr->hPtr != NULL) { -        Tcl_DeleteHashEntry(cmdPtr->hPtr); -        cmdPtr->hPtr = NULL; +	Tcl_DeleteHashEntry(cmdPtr->hPtr); +	cmdPtr->hPtr = NULL;      }      /* -     * Now link the hash table entry with the command structure. -     * This is like creating a new command, so deal with any shadowing -     * of commands in the global namespace. +     * Now link the hash table entry with the command structure. This is like +     * creating a new command, so deal with any shadowing of commands in the +     * global namespace.       */ -     +      cmdPtr->hPtr = hPtr; -    Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); +    Tcl_SetHashValue(hPtr, cmdPtr);      /* -     * Not needed as we are only in the global namespace -     * (but would be needed again if we supported namespace command hiding) +     * Not needed as we are only in the global namespace (but would be needed +     * again if we supported namespace command hiding)       *       * TclResetShadowedCmdRefs(interp, cmdPtr);       */ -      /* -     * If the command being exposed has a compile procedure, increment -     * interpreter's compileEpoch to invalidate its compiled code. This -     * makes sure that we don't later try to execute old code compiled -     * assuming the command is hidden. This field is checked in Tcl_EvalObj -     * and ObjInterpProc, and code whose compilation epoch doesn't match is +     * If the command being exposed has a compile function, increment +     * interpreter's compileEpoch to invalidate its compiled code. This makes +     * sure that we don't later try to execute old code compiled assuming the +     * command is hidden. This field is checked in Tcl_EvalObj and +     * ObjInterpProc, and code whose compilation epoch doesn't match is       * recompiled.       */ @@ -1570,104 +2011,134 @@ Tcl_ExposeCommand(interp, hiddenCmdToken, cmdName)   *	Define a new command in a command table.   *   * Results: - *	The return value is a token for the command, which can - *	be used in future calls to Tcl_GetCommandName. + *	The return value is a token for the command, which can be used in + *	future calls to Tcl_GetCommandName.   *   * Side effects:   *	If a command named cmdName already exists for interp, it is deleted.   *	In the future, when cmdName is seen as the name of a command by   *	Tcl_Eval, proc will be called. To support the bytecode interpreter,   *	the command is created with a wrapper Tcl_ObjCmdProc - *	(TclInvokeStringCommand) that eventially calls proc. When the - *	command is deleted from the table, deleteProc will be called. - *	See the manual entry for details on the calling sequence. + *	(TclInvokeStringCommand) that eventially calls proc. When the command + *	is deleted from the table, deleteProc will be called. See the manual + *	entry for details on the calling sequence.   *   *----------------------------------------------------------------------   */  Tcl_Command -Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc) -    Tcl_Interp *interp;		/* Token for command interpreter returned by -				 * a previous call to Tcl_CreateInterp. */ -    CONST char *cmdName;	/* Name of command. If it contains namespace +Tcl_CreateCommand( +    Tcl_Interp *interp,		/* Token for command interpreter returned by a +				 * previous call to Tcl_CreateInterp. */ +    const char *cmdName,	/* Name of command. If it contains namespace  				 * qualifiers, the new command is put in the -				 * specified namespace; otherwise it is put -				 * in the global namespace. */ -    Tcl_CmdProc *proc;		/* Procedure to associate with cmdName. */ -    ClientData clientData;	/* Arbitrary value passed to string proc. */ -    Tcl_CmdDeleteProc *deleteProc; -				/* If not NULL, gives a procedure to call -				 * when this command is deleted. */ +				 * specified namespace; otherwise it is put in +				 * the global namespace. */ +    Tcl_CmdProc *proc,		/* Function to associate with cmdName. */ +    ClientData clientData,	/* Arbitrary value passed to string proc. */ +    Tcl_CmdDeleteProc *deleteProc) +				/* If not NULL, gives a function to call when +				 * this command is deleted. */  {      Interp *iPtr = (Interp *) interp;      ImportRef *oldRefPtr = NULL;      Namespace *nsPtr, *dummy1, *dummy2;      Command *cmdPtr, *refCmdPtr;      Tcl_HashEntry *hPtr; -    CONST char *tail; -    int new; +    const char *tail; +    int isNew;      ImportedCmdData *dataPtr;      if (iPtr->flags & DELETED) {  	/* -	 * The interpreter is being deleted.  Don't create any new -	 * commands; it's not safe to muck with the interpreter anymore. +	 * The interpreter is being deleted. Don't create any new commands; +	 * it's not safe to muck with the interpreter anymore.  	 */  	return (Tcl_Command) NULL;      }      /* -     * Determine where the command should reside. If its name contains  -     * namespace qualifiers, we put it in the specified namespace;  -     * otherwise, we always put it in the global namespace. +     * Determine where the command should reside. If its name contains +     * namespace qualifiers, we put it in the specified namespace; otherwise, +     * we always put it in the global namespace.       */      if (strstr(cmdName, "::") != NULL) { -       TclGetNamespaceForQualName(interp, cmdName, (Namespace *) NULL, -           CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); -       if ((nsPtr == NULL) || (tail == NULL)) { +	TclGetNamespaceForQualName(interp, cmdName, NULL, +		TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); +	if ((nsPtr == NULL) || (tail == NULL)) {  	    return (Tcl_Command) NULL;  	}      } else {  	nsPtr = iPtr->globalNsPtr;  	tail = cmdName;      } -     -    hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); -    if (!new) { + +    hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); +    if (!isNew) {  	/* -	 * Command already exists. Delete the old one. -	 * Be careful to preserve any existing import links so we can -	 * restore them down below.  That way, you can redefine a -	 * command and its import status will remain intact. +	 * Command already exists. Delete the old one. Be careful to preserve +	 * any existing import links so we can restore them down below. That +	 * way, you can redefine a command and its import status will remain +	 * intact.  	 */ -	cmdPtr = (Command *) Tcl_GetHashValue(hPtr); -	oldRefPtr = cmdPtr->importRefPtr; -	cmdPtr->importRefPtr = NULL; +	cmdPtr = Tcl_GetHashValue(hPtr); +	cmdPtr->refCount++; +	if (cmdPtr->importRefPtr) { +	    cmdPtr->flags |= CMD_REDEF_IN_PROGRESS; +	}  	Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); -	hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); -	if (!new) { + +	if (cmdPtr->flags & CMD_REDEF_IN_PROGRESS) { +	    oldRefPtr = cmdPtr->importRefPtr; +	    cmdPtr->importRefPtr = NULL; +	} +	TclCleanupCommandMacro(cmdPtr); + +	hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); +	if (!isNew) {  	    /* -	     * If the deletion callback recreated the command, just throw -             * away the new command (if we try to delete it again, we -             * could get stuck in an infinite loop). +	     * If the deletion callback recreated the command, just throw away +	     * the new command (if we try to delete it again, we could get +	     * stuck in an infinite loop).  	     */ -	     ckfree((char*) Tcl_GetHashValue(hPtr)); +	    ckfree(Tcl_GetHashValue(hPtr));  	} +    } else { +	/* +	 * 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); + +	/* +	 * The list of command exported from the namespace might have changed. +	 * However, we do not need to recompute this just yet; next time we +	 * need the info will be soon enough. +	 */ + +	TclInvalidateNsCmdLookup(nsPtr); +	TclInvalidateNsPath(nsPtr);      } -    cmdPtr = (Command *) ckalloc(sizeof(Command)); +    cmdPtr = ckalloc(sizeof(Command));      Tcl_SetHashValue(hPtr, cmdPtr);      cmdPtr->hPtr = hPtr;      cmdPtr->nsPtr = nsPtr;      cmdPtr->refCount = 1;      cmdPtr->cmdEpoch = 0; -    cmdPtr->compileProc = (CompileProc *) NULL; +    cmdPtr->compileProc = NULL;      cmdPtr->objProc = TclInvokeStringCommand; -    cmdPtr->objClientData = (ClientData) cmdPtr; +    cmdPtr->objClientData = cmdPtr;      cmdPtr->proc = proc;      cmdPtr->clientData = clientData;      cmdPtr->deleteProc = deleteProc; @@ -1675,17 +2146,18 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)      cmdPtr->flags = 0;      cmdPtr->importRefPtr = NULL;      cmdPtr->tracePtr = NULL; +    cmdPtr->nreProc = NULL;      /* -     * Plug in any existing import references found above.  Be sure -     * to update all of these references to point to the new command. +     * Plug in any existing import references found above. Be sure to update +     * all of these references to point to the new command.       */      if (oldRefPtr != NULL) {  	cmdPtr->importRefPtr = oldRefPtr;  	while (oldRefPtr != NULL) {  	    refCmdPtr = oldRefPtr->importedCmdPtr; -	    dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; +	    dataPtr = refCmdPtr->objClientData;  	    dataPtr->realCmdPtr = cmdPtr;  	    oldRefPtr = oldRefPtr->nextPtr;  	} @@ -1697,7 +2169,7 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)       * shadowed commands are found, invalidate all cached command references       * in the affected namespaces.       */ -     +      TclResetShadowedCmdRefs(interp, cmdPtr);      return (Tcl_Command) cmdPtr;  } @@ -1710,70 +2182,67 @@ Tcl_CreateCommand(interp, cmdName, proc, clientData, deleteProc)   *	Define a new object-based command in a command table.   *   * Results: - *	The return value is a token for the command, which can - *	be used in future calls to Tcl_GetCommandName. + *	The return value is a token for the command, which can be used in + *	future calls to Tcl_GetCommandName.   *   * Side effects: - *	If no command named "cmdName" already exists for interp, one is - *	created. Otherwise, if a command does exist, then if the - *	object-based Tcl_ObjCmdProc is TclInvokeStringCommand, we assume - *	Tcl_CreateCommand was called previously for the same command and - *	just set its Tcl_ObjCmdProc to the argument "proc"; otherwise, we - *	delete the old command. + *	If a command named "cmdName" already exists for interp, it is + *	first deleted.  Then the new command is created from the arguments. + *	[***] (See below for exception).   *   *	In the future, during bytecode evaluation when "cmdName" is seen as   *	the name of a command by Tcl_EvalObj or Tcl_Eval, the object-based   *	Tcl_ObjCmdProc proc will be called. When the command is deleted from - *	the table, deleteProc will be called. See the manual entry for - *	details on the calling sequence. + *	the table, deleteProc will be called. See the manual entry for details + *	on the calling sequence.   *   *----------------------------------------------------------------------   */  Tcl_Command -Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc) -    Tcl_Interp *interp;		/* Token for command interpreter (returned -				 * by previous call to Tcl_CreateInterp). */ -    CONST char *cmdName;	/* Name of command. If it contains namespace +Tcl_CreateObjCommand( +    Tcl_Interp *interp,		/* Token for command interpreter (returned by +				 * previous call to Tcl_CreateInterp). */ +    const char *cmdName,	/* Name of command. If it contains namespace  				 * qualifiers, the new command is put in the -				 * specified namespace; otherwise it is put -				 * in the global namespace. */ -    Tcl_ObjCmdProc *proc;	/* Object-based procedure to associate with +				 * specified namespace; otherwise it is put in +				 * the global namespace. */ +    Tcl_ObjCmdProc *proc,	/* Object-based function to associate with  				 * name. */ -    ClientData clientData;	/* Arbitrary value to pass to object -    				 * procedure. */ -    Tcl_CmdDeleteProc *deleteProc; -				/* If not NULL, gives a procedure to call -				 * when this command is deleted. */ +    ClientData clientData,	/* Arbitrary value to pass to object +				 * function. */ +    Tcl_CmdDeleteProc *deleteProc) +				/* If not NULL, gives a function to call when +				 * this command is deleted. */  {      Interp *iPtr = (Interp *) interp;      ImportRef *oldRefPtr = NULL;      Namespace *nsPtr, *dummy1, *dummy2;      Command *cmdPtr, *refCmdPtr;      Tcl_HashEntry *hPtr; -    CONST char *tail; -    int new; +    const char *tail; +    int isNew;      ImportedCmdData *dataPtr;      if (iPtr->flags & DELETED) {  	/* -	 * The interpreter is being deleted.  Don't create any new -	 * commands;  it's not safe to muck with the interpreter anymore. +	 * The interpreter is being deleted. Don't create any new commands; +	 * it's not safe to muck with the interpreter anymore.  	 */  	return (Tcl_Command) NULL;      }      /* -     * Determine where the command should reside. If its name contains  -     * namespace qualifiers, we put it in the specified namespace;  -     * otherwise, we always put it in the global namespace. +     * Determine where the command should reside. If its name contains +     * namespace qualifiers, we put it in the specified namespace; otherwise, +     * we always put it in the global namespace.       */      if (strstr(cmdName, "::") != NULL) { -       TclGetNamespaceForQualName(interp, cmdName, (Namespace *) NULL, -           CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); -       if ((nsPtr == NULL) || (tail == NULL)) { +	TclGetNamespaceForQualName(interp, cmdName, NULL, +		TCL_CREATE_NS_IF_UNKNOWN, &nsPtr, &dummy1, &dummy2, &tail); +	if ((nsPtr == NULL) || (tail == NULL)) {  	    return (Tcl_Command) NULL;  	}      } else { @@ -1781,85 +2250,121 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)  	tail = cmdName;      } -    hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); -    if (!new) { -	cmdPtr = (Command *) Tcl_GetHashValue(hPtr); +    hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); +    TclInvalidateNsPath(nsPtr); +    if (!isNew) { +	cmdPtr = Tcl_GetHashValue(hPtr); + +	/* Command already exists. */  	/* -	 * Command already exists. If its object-based Tcl_ObjCmdProc is -	 * TclInvokeStringCommand, we just set its Tcl_ObjCmdProc to the -	 * argument "proc". Otherwise, we delete the old command.  +	 * [***] 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) { +	if (cmdPtr->objProc == TclInvokeStringCommand +		&& cmdPtr->clientData == clientData +		&& cmdPtr->deleteData == clientData +		&& cmdPtr->deleteProc == deleteProc) {  	    cmdPtr->objProc = proc;  	    cmdPtr->objClientData = clientData; -            cmdPtr->deleteProc = deleteProc; -            cmdPtr->deleteData = clientData;  	    return (Tcl_Command) cmdPtr;  	}  	/* -	 * Otherwise, we delete the old command.  Be careful to preserve -	 * any existing import links so we can restore them down below. -	 * That way, you can redefine a command and its import status -	 * will remain intact. +	 * Otherwise, we delete the old command. Be careful to preserve any +	 * existing import links so we can restore them down below. That way, +	 * you can redefine a command and its import status will remain +	 * intact.  	 */ -	oldRefPtr = cmdPtr->importRefPtr; -	cmdPtr->importRefPtr = NULL; +	cmdPtr->refCount++; +	if (cmdPtr->importRefPtr) { +	    cmdPtr->flags |= CMD_REDEF_IN_PROGRESS; +	}  	Tcl_DeleteCommandFromToken(interp, (Tcl_Command) cmdPtr); -	hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &new); -	if (!new) { + +	if (cmdPtr->flags & CMD_REDEF_IN_PROGRESS) { +	    oldRefPtr = cmdPtr->importRefPtr; +	    cmdPtr->importRefPtr = NULL; +	} +	TclCleanupCommandMacro(cmdPtr); + +	hPtr = Tcl_CreateHashEntry(&nsPtr->cmdTable, tail, &isNew); +	if (!isNew) {  	    /* -	     * If the deletion callback recreated the command, just throw -	     * away the new command (if we try to delete it again, we -	     * could get stuck in an infinite loop). +	     * If the deletion callback recreated the command, just throw away +	     * the new command (if we try to delete it again, we could get +	     * stuck in an infinite loop).  	     */ -	     ckfree((char *) Tcl_GetHashValue(hPtr)); +	    ckfree(Tcl_GetHashValue(hPtr));  	} +    } else { +	/* +	 * 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); + +	/* +	 * The list of command exported from the namespace might have changed. +	 * However, we do not need to recompute this just yet; next time we +	 * need the info will be soon enough. +	 */ + +	TclInvalidateNsCmdLookup(nsPtr);      } -    cmdPtr = (Command *) ckalloc(sizeof(Command)); +    cmdPtr = ckalloc(sizeof(Command));      Tcl_SetHashValue(hPtr, cmdPtr);      cmdPtr->hPtr = hPtr;      cmdPtr->nsPtr = nsPtr;      cmdPtr->refCount = 1;      cmdPtr->cmdEpoch = 0; -    cmdPtr->compileProc = (CompileProc *) NULL; +    cmdPtr->compileProc = NULL;      cmdPtr->objProc = proc;      cmdPtr->objClientData = clientData;      cmdPtr->proc = TclInvokeObjectCommand; -    cmdPtr->clientData = (ClientData) cmdPtr; +    cmdPtr->clientData = cmdPtr;      cmdPtr->deleteProc = deleteProc;      cmdPtr->deleteData = clientData;      cmdPtr->flags = 0;      cmdPtr->importRefPtr = NULL;      cmdPtr->tracePtr = NULL; +    cmdPtr->nreProc = NULL;      /* -     * Plug in any existing import references found above.  Be sure -     * to update all of these references to point to the new command. +     * Plug in any existing import references found above. Be sure to update +     * all of these references to point to the new command.       */      if (oldRefPtr != NULL) {  	cmdPtr->importRefPtr = oldRefPtr;  	while (oldRefPtr != NULL) {  	    refCmdPtr = oldRefPtr->importedCmdPtr; -	    dataPtr = (ImportedCmdData*)refCmdPtr->objClientData; +	    dataPtr = refCmdPtr->objClientData;  	    dataPtr->realCmdPtr = cmdPtr;  	    oldRefPtr = oldRefPtr->nextPtr;  	}      } -     +      /*       * We just created a command, so in its namespace and all of its parent       * namespaces, it may shadow global commands with the same name. If any       * shadowed commands are found, invalidate all cached command references       * in the affected namespaces.       */ -     +      TclResetShadowedCmdRefs(interp, cmdPtr);      return (Tcl_Command) cmdPtr;  } @@ -1870,10 +2375,10 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)   * TclInvokeStringCommand --   *   *	"Wrapper" Tcl_ObjCmdProc used to call an existing string-based - *	Tcl_CmdProc if no object-based procedure exists for a command. A - *	pointer to this procedure is stored as the Tcl_ObjCmdProc in a - *	Command structure. It simply turns around and calls the string - *	Tcl_CmdProc in the Command structure. + *	Tcl_CmdProc if no object-based function exists for a command. A + *	pointer to this function is stored as the Tcl_ObjCmdProc in a Command + *	structure. It simply turns around and calls the string Tcl_CmdProc in + *	the Command structure.   *   * Results:   *	A standard Tcl object result value. @@ -1886,37 +2391,18 @@ Tcl_CreateObjCommand(interp, cmdName, proc, clientData, deleteProc)   */  int -TclInvokeStringCommand(clientData, interp, objc, objv) -    ClientData clientData;	/* Points to command's Command structure. */ -    Tcl_Interp *interp;		/* Current interpreter. */ -    register int objc;		/* Number of arguments. */ -    Tcl_Obj *CONST objv[];	/* Argument objects. */ -{ -    register Command *cmdPtr = (Command *) clientData; -    register int i; -    int result; - -    /* -     * This procedure generates an argv array for the string arguments. It -     * starts out with stack-allocated space but uses dynamically-allocated -     * storage if needed. -     */ - -#define NUM_ARGS 20 -    CONST char *(argStorage[NUM_ARGS]); -    CONST char **argv = argStorage; - -    /* -     * Create the string argument array "argv". Make sure argv is large -     * enough to hold the objc arguments plus 1 extra for the zero -     * end-of-argv word. -     */ - -    if ((objc + 1) > NUM_ARGS) { -	argv = (CONST char **) ckalloc((unsigned)(objc + 1) * sizeof(char *)); -    } +TclInvokeStringCommand( +    ClientData clientData,	/* Points to command's Command structure. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    register int objc,		/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    Command *cmdPtr = clientData; +    int i, result; +    const char **argv = +	    TclStackAlloc(interp, (unsigned)(objc + 1) * sizeof(char *)); -    for (i = 0;  i < objc;  i++) { +    for (i = 0; i < objc; i++) {  	argv[i] = Tcl_GetString(objv[i]);      }      argv[objc] = 0; @@ -1925,17 +2411,10 @@ TclInvokeStringCommand(clientData, interp, objc, objv)       * Invoke the command's string-based Tcl_CmdProc.       */ -    result = (*cmdPtr->proc)(cmdPtr->clientData, interp, objc, argv); +    result = cmdPtr->proc(cmdPtr->clientData, interp, objc, argv); -    /* -     * Free the argv array if malloc'ed storage was used. -     */ - -    if (argv != argStorage) { -	ckfree((char *) argv); -    } +    TclStackFree(interp, (void *) argv);      return result; -#undef NUM_ARGS  }  /* @@ -1944,58 +2423,37 @@ TclInvokeStringCommand(clientData, interp, objc, objv)   * TclInvokeObjectCommand --   *   *	"Wrapper" Tcl_CmdProc used to call an existing object-based - *	Tcl_ObjCmdProc if no string-based procedure exists for a command. - *	A pointer to this procedure is stored as the Tcl_CmdProc in a - *	Command structure. It simply turns around and calls the object - *	Tcl_ObjCmdProc in the Command structure. + *	Tcl_ObjCmdProc if no string-based function exists for a command. A + *	pointer to this function is stored as the Tcl_CmdProc in a Command + *	structure. It simply turns around and calls the object Tcl_ObjCmdProc + *	in the Command structure.   *   * Results:   *	A standard Tcl string result value.   *   * Side effects: - *	Besides those side effects of the called Tcl_CmdProc, - *	TclInvokeStringCommand allocates and frees storage. + *	Besides those side effects of the called Tcl_ObjCmdProc, + *	TclInvokeObjectCommand allocates and frees storage.   *   *----------------------------------------------------------------------   */  int -TclInvokeObjectCommand(clientData, interp, argc, argv) -    ClientData clientData;	/* Points to command's Command structure. */ -    Tcl_Interp *interp;		/* Current interpreter. */ -    int argc;			/* Number of arguments. */ -    register CONST char **argv;	/* Argument strings. */ +TclInvokeObjectCommand( +    ClientData clientData,	/* Points to command's Command structure. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int argc,			/* Number of arguments. */ +    register const char **argv)	/* Argument strings. */  { -    Command *cmdPtr = (Command *) clientData; -    register Tcl_Obj *objPtr; -    register int i; -    int length, result; - -    /* -     * This procedure generates an objv array for object arguments that hold -     * the argv strings. It starts out with stack-allocated space but uses -     * dynamically-allocated storage if needed. -     */ - -#define NUM_ARGS 20 -    Tcl_Obj *(argStorage[NUM_ARGS]); -    register Tcl_Obj **objv = argStorage; - -    /* -     * Create the object argument array "objv". Make sure objv is large -     * enough to hold the objc arguments plus 1 extra for the zero -     * end-of-objv word. -     */ - -    if (argc > NUM_ARGS) { -	objv = (Tcl_Obj **) -	    ckalloc((unsigned)(argc * sizeof(Tcl_Obj *))); -    } +    Command *cmdPtr = clientData; +    Tcl_Obj *objPtr; +    int i, length, result; +    Tcl_Obj **objv = +	    TclStackAlloc(interp, (unsigned)(argc * sizeof(Tcl_Obj *))); -    for (i = 0;  i < argc;  i++) { +    for (i = 0; i < argc; i++) {  	length = strlen(argv[i]); -	TclNewObj(objPtr); -	TclInitStringRep(objPtr, argv[i], length); +	TclNewStringObj(objPtr, argv[i], length);  	Tcl_IncrRefCount(objPtr);  	objv[i] = objPtr;      } @@ -2004,30 +2462,31 @@ TclInvokeObjectCommand(clientData, interp, argc, argv)       * Invoke the command's object-based Tcl_ObjCmdProc.       */ -    result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, argc, objv); +    if (cmdPtr->objProc != NULL) { +	result = cmdPtr->objProc(cmdPtr->objClientData, interp, argc, objv); +    } else { +	result = Tcl_NRCallObjProc(interp, cmdPtr->nreProc, +		cmdPtr->objClientData, argc, objv); +    }      /* -     * Move the interpreter's object result to the string result,  -     * then reset the object result. +     * Move the interpreter's object result to the string result, then reset +     * the object result.       */ -    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	    TCL_VOLATILE); -     +    (void) Tcl_GetStringResult(interp); +      /* -     * Decrement the ref counts for the argument objects created above, -     * then free the objv array if malloc'ed storage was used. +     * Decrement the ref counts for the argument objects created above, then +     * free the objv array if malloc'ed storage was used.       */ -    for (i = 0;  i < argc;  i++) { +    for (i = 0; i < argc; i++) {  	objPtr = objv[i];  	Tcl_DecrRefCount(objPtr);      } -    if (objv != argStorage) { -	ckfree((char *) objv); -    } +    TclStackFree(interp, objv);      return result; -#undef NUM_ARGS  }  /* @@ -2035,65 +2494,66 @@ TclInvokeObjectCommand(clientData, interp, argc, argv)   *   * TclRenameCommand --   * - *      Called to give an existing Tcl command a different name. Both the - *      old command name and the new command name can have "::" namespace - *      qualifiers. If the new command has a different namespace context, - *      the command will be moved to that namespace and will execute in - *	the context of that new namespace. + *	Called to give an existing Tcl command a different name. Both the old + *	command name and the new command name can have "::" namespace + *	qualifiers. If the new command has a different namespace context, the + *	command will be moved to that namespace and will execute in the + *	context of that new namespace.   * - *      If the new command name is NULL or the null string, the command is - *      deleted. + *	If the new command name is NULL or the null string, the command is + *	deleted.   *   * Results: - *      Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong. + *	Returns TCL_OK if successful, and TCL_ERROR if anything goes wrong.   *   * Side effects: - *      If anything goes wrong, an error message is returned in the - *      interpreter's result object. + *	If anything goes wrong, an error message is returned in the + *	interpreter's result object.   *   *----------------------------------------------------------------------   */  int -TclRenameCommand(interp, oldName, newName) -    Tcl_Interp *interp;                 /* Current interpreter. */ -    char *oldName;                      /* Existing command name. */ -    char *newName;                      /* New command name. */ +TclRenameCommand( +    Tcl_Interp *interp,		/* Current interpreter. */ +    const char *oldName,	/* Existing command name. */ +    const char *newName)	/* New command name. */  {      Interp *iPtr = (Interp *) interp; -    CONST char *newTail; +    const char *newTail;      Namespace *cmdNsPtr, *newNsPtr, *dummy1, *dummy2;      Tcl_Command cmd;      Command *cmdPtr;      Tcl_HashEntry *hPtr, *oldHPtr; -    int new, result; -    Tcl_Obj* oldFullName; +    int isNew, result; +    Tcl_Obj *oldFullName;      Tcl_DString newFullName;      /* -     * Find the existing command. An error is returned if cmdName can't -     * be found. +     * Find the existing command. An error is returned if cmdName can't be +     * found.       */ -    cmd = Tcl_FindCommand(interp, oldName, (Tcl_Namespace *) NULL, -	/*flags*/ 0); +    cmd = Tcl_FindCommand(interp, oldName, NULL, /*flags*/ 0);      cmdPtr = (Command *) cmd;      if (cmdPtr == NULL) { -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "can't ", -                ((newName == NULL)||(*newName == '\0'))? "delete":"rename", -                " \"", oldName, "\": command doesn't exist", (char *) NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can't %s \"%s\": command doesn't exist", +		((newName == NULL)||(*newName == '\0'))? "delete":"rename", +		oldName)); +        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COMMAND", oldName, NULL);  	return TCL_ERROR;      }      cmdNsPtr = cmdPtr->nsPtr;      oldFullName = Tcl_NewObj(); -    Tcl_IncrRefCount( oldFullName ); -    Tcl_GetCommandFullName( interp, cmd, oldFullName ); +    Tcl_IncrRefCount(oldFullName); +    Tcl_GetCommandFullName(interp, cmd, oldFullName);      /*       * If the new command name is NULL or empty, delete the command. Do this       * with Tcl_DeleteCommandFromToken, since we already have the command.       */ -     +      if ((newName == NULL) || (*newName == '\0')) {  	Tcl_DeleteCommandFromToken(interp, cmd);  	result = TCL_OK; @@ -2101,101 +2561,120 @@ TclRenameCommand(interp, oldName, newName)      }      /* -     * Make sure that the destination command does not already exist. -     * The rename operation is like creating a command, so we should -     * automatically create the containing namespaces just like -     * Tcl_CreateCommand would. +     * Make sure that the destination command does not already exist. The +     * rename operation is like creating a command, so we should automatically +     * create the containing namespaces just like Tcl_CreateCommand would.       */ -    TclGetNamespaceForQualName(interp, newName, (Namespace *) NULL, -       CREATE_NS_IF_UNKNOWN, &newNsPtr, &dummy1, &dummy2, &newTail); +    TclGetNamespaceForQualName(interp, newName, NULL, +	    TCL_CREATE_NS_IF_UNKNOWN, &newNsPtr, &dummy1, &dummy2, &newTail);      if ((newNsPtr == NULL) || (newTail == NULL)) { -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		 "can't rename to \"", newName, "\": bad command name", -    	    	 (char *) NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can't rename to \"%s\": bad command name", newName)); +        Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", NULL);  	result = TCL_ERROR;  	goto done;      }      if (Tcl_FindHashEntry(&newNsPtr->cmdTable, newTail) != NULL) { -	Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		 "can't rename to \"", newName, -		 "\": command already exists", (char *) NULL); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can't rename to \"%s\": command already exists", newName)); +        Tcl_SetErrorCode(interp, "TCL", "OPERATION", "RENAME", +                "TARGET_EXISTS", NULL);  	result = TCL_ERROR;  	goto done;      }      /* -     * Warning: any changes done in the code here are likely -     * to be needed in Tcl_HideCommand() code too. -     * (until the common parts are extracted out)     --dl +     * Warning: any changes done in the code here are likely to be needed in +     * Tcl_HideCommand code too (until the common parts are extracted out). +     * - dl       */      /* -     * Put the command in the new namespace so we can check for an alias -     * loop. Since we are adding a new command to a namespace, we must -     * handle any shadowing of the global commands that this might create. +     * Put the command in the new namespace so we can check for an alias loop. +     * Since we are adding a new command to a namespace, we must handle any +     * shadowing of the global commands that this might create.       */ -     +      oldHPtr = cmdPtr->hPtr; -    hPtr = Tcl_CreateHashEntry(&newNsPtr->cmdTable, newTail, &new); -    Tcl_SetHashValue(hPtr, (ClientData) cmdPtr); +    hPtr = Tcl_CreateHashEntry(&newNsPtr->cmdTable, newTail, &isNew); +    Tcl_SetHashValue(hPtr, cmdPtr);      cmdPtr->hPtr = hPtr;      cmdPtr->nsPtr = newNsPtr;      TclResetShadowedCmdRefs(interp, cmdPtr);      /* -     * Now check for an alias loop. If we detect one, put everything back -     * the way it was and report the error. +     * Now check for an alias loop. If we detect one, put everything back the +     * way it was and report the error.       */      result = TclPreventAliasLoop(interp, interp, (Tcl_Command) cmdPtr);      if (result != TCL_OK) { -        Tcl_DeleteHashEntry(cmdPtr->hPtr); -        cmdPtr->hPtr = oldHPtr; -        cmdPtr->nsPtr = cmdNsPtr; +	Tcl_DeleteHashEntry(cmdPtr->hPtr); +	cmdPtr->hPtr = oldHPtr; +	cmdPtr->nsPtr = cmdNsPtr;  	goto done;      }      /* -     * Script for rename traces can delete the command "oldName". -     * Therefore increment the reference count for cmdPtr so that -     * it's Command structure is freed only towards the end of this -     * function by calling TclCleanupCommand. +     * The list of command exported from the namespace might have changed. +     * However, we do not need to recompute this just yet; next time we need +     * the info will be soon enough. These might refer to the same variable, +     * but that's no big deal. +     */ + +    TclInvalidateNsCmdLookup(cmdNsPtr); +    TclInvalidateNsCmdLookup(cmdPtr->nsPtr); + +    /* +     * 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 +     * TclCleanupCommand.       * -     * The trace procedure needs to get a fully qualified name for -     * old and new commands [Tcl bug #651271], or else there's no way -     * for the trace procedure to get the namespace from which the old -     * command is being renamed! +     * The trace function needs to get a fully qualified name for old and new +     * commands [Tcl bug #651271], or else there's no way for the trace +     * function to get the namespace from which the old command is being +     * renamed!       */ -    Tcl_DStringInit( &newFullName ); -    Tcl_DStringAppend( &newFullName, newNsPtr->fullName, -1 ); -    if ( newNsPtr != iPtr->globalNsPtr ) { -	Tcl_DStringAppend( &newFullName, "::", 2 ); +    Tcl_DStringInit(&newFullName); +    Tcl_DStringAppend(&newFullName, newNsPtr->fullName, -1); +    if (newNsPtr != iPtr->globalNsPtr) { +	TclDStringAppendLiteral(&newFullName, "::");      } -    Tcl_DStringAppend( &newFullName, newTail, -1 ); +    Tcl_DStringAppend(&newFullName, newTail, -1);      cmdPtr->refCount++; -    CallCommandTraces( iPtr, cmdPtr, -		       Tcl_GetString( oldFullName ), -		       Tcl_DStringValue( &newFullName ), -		       TCL_TRACE_RENAME); -    Tcl_DStringFree( &newFullName ); +    CallCommandTraces(iPtr, cmdPtr, Tcl_GetString(oldFullName), +	    Tcl_DStringValue(&newFullName), TCL_TRACE_RENAME); +    Tcl_DStringFree(&newFullName);      /* -     * The new command name is okay, so remove the command from its -     * current namespace. This is like deleting the command, so bump -     * the cmdEpoch to invalidate any cached references to the command. +     * The new command name is okay, so remove the command from its current +     * namespace. This is like deleting the command, so bump the cmdEpoch to +     * invalidate any cached references to the command.       */ -     +      Tcl_DeleteHashEntry(oldHPtr);      cmdPtr->cmdEpoch++;      /* -     * If the command being renamed has a compile procedure, increment the -     * interpreter's compileEpoch to invalidate its compiled code. This -     * makes sure that we don't later try to execute old code compiled for -     * the now-renamed command. +     * If the command being renamed has a compile function, increment the +     * interpreter's compileEpoch to invalidate its compiled code. This makes +     * sure that we don't later try to execute old code compiled for the +     * now-renamed command.       */      if (cmdPtr->compileProc != NULL) { @@ -2203,14 +2682,15 @@ TclRenameCommand(interp, oldName, newName)      }      /* -     * Now free the Command structure, if the "oldName" command has -     * been deleted by invocation of rename traces. +     * Now free the Command structure, if the "oldName" command has been +     * deleted by invocation of rename traces.       */ -    TclCleanupCommand(cmdPtr); + +    TclCleanupCommandMacro(cmdPtr);      result = TCL_OK; -    done: -    TclDecrRefCount( oldFullName ); +  done: +    TclDecrRefCount(oldFullName);      return result;  } @@ -2219,16 +2699,15 @@ TclRenameCommand(interp, oldName, newName)   *   * Tcl_SetCommandInfo --   * - *	Modifies various information about a Tcl command. Note that - *	this procedure will not change a command's namespace; use - *	Tcl_RenameCommand to do that. Also, the isNativeObjectProc - *	member of *infoPtr is ignored. + *	Modifies various information about a Tcl command. Note that this + *	function will not change a command's namespace; use TclRenameCommand + *	to do that. Also, the isNativeObjectProc member of *infoPtr is + *	ignored.   *   * Results: - *	If cmdName exists in interp, then the information at *infoPtr - *	is stored with the command in place of the current information - *	and 1 is returned. If the command doesn't exist then 0 is - *	returned.  + *	If cmdName exists in interp, then the information at *infoPtr is + *	stored with the command in place of the current information and 1 is + *	returned. If the command doesn't exist then 0 is returned.   *   * Side effects:   *	None. @@ -2237,20 +2716,17 @@ TclRenameCommand(interp, oldName, newName)   */  int -Tcl_SetCommandInfo(interp, cmdName, infoPtr) -    Tcl_Interp *interp;			/* Interpreter in which to look -					 * for command. */ -    CONST char *cmdName;		/* Name of desired command. */ -    CONST Tcl_CmdInfo *infoPtr;		/* Where to find information -					 * to store in the command. */ +Tcl_SetCommandInfo( +    Tcl_Interp *interp,		/* Interpreter in which to look for +				 * command. */ +    const char *cmdName,	/* Name of desired command. */ +    const Tcl_CmdInfo *infoPtr)	/* Where to find information to store in the +				 * command. */  {      Tcl_Command cmd; -    cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, -            /*flags*/ 0); - -    return Tcl_SetCommandInfoFromToken( cmd, infoPtr ); - +    cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); +    return Tcl_SetCommandInfoFromToken(cmd, infoPtr);  }  /* @@ -2258,16 +2734,15 @@ Tcl_SetCommandInfo(interp, cmdName, infoPtr)   *   * Tcl_SetCommandInfoFromToken --   * - *	Modifies various information about a Tcl command. Note that - *	this procedure will not change a command's namespace; use - *	Tcl_RenameCommand to do that. Also, the isNativeObjectProc - *	member of *infoPtr is ignored. + *	Modifies various information about a Tcl command. Note that this + *	function will not change a command's namespace; use TclRenameCommand + *	to do that. Also, the isNativeObjectProc member of *infoPtr is + *	ignored.   *   * Results: - *	If cmdName exists in interp, then the information at *infoPtr - *	is stored with the command in place of the current information - *	and 1 is returned. If the command doesn't exist then 0 is - *	returned.  + *	If cmdName exists in interp, then the information at *infoPtr is + *	stored with the command in place of the current information and 1 is + *	returned. If the command doesn't exist then 0 is returned.   *   * Side effects:   *	None. @@ -2276,28 +2751,32 @@ Tcl_SetCommandInfo(interp, cmdName, infoPtr)   */  int -Tcl_SetCommandInfoFromToken( cmd, infoPtr ) -    Tcl_Command cmd; -    CONST Tcl_CmdInfo* infoPtr; +Tcl_SetCommandInfoFromToken( +    Tcl_Command cmd, +    const Tcl_CmdInfo *infoPtr)  { -    Command* cmdPtr;		/* Internal representation of the command */ +    Command *cmdPtr;		/* Internal representation of the command */ -    if (cmd == (Tcl_Command) NULL) { +    if (cmd == NULL) {  	return 0;      }      /*       * The isNativeObjectProc and nsPtr members of *infoPtr are ignored.       */ -     +      cmdPtr = (Command *) cmd;      cmdPtr->proc = infoPtr->proc;      cmdPtr->clientData = infoPtr->clientData; -    if (infoPtr->objProc == (Tcl_ObjCmdProc *) NULL) { +    if (infoPtr->objProc == NULL) {  	cmdPtr->objProc = TclInvokeStringCommand; -	cmdPtr->objClientData = (ClientData) cmdPtr; +	cmdPtr->objClientData = cmdPtr; +	cmdPtr->nreProc = NULL;      } else { -	cmdPtr->objProc = infoPtr->objProc; +	if (infoPtr->objProc != cmdPtr->objProc) { +	    cmdPtr->nreProc = NULL; +	    cmdPtr->objProc = infoPtr->objProc; +	}  	cmdPtr->objClientData = infoPtr->objClientData;      }      cmdPtr->deleteProc = infoPtr->deleteProc; @@ -2313,10 +2792,9 @@ Tcl_SetCommandInfoFromToken( cmd, infoPtr )   *	Returns various information about a Tcl command.   *   * Results: - *	If cmdName exists in interp, then *infoPtr is modified to - *	hold information about cmdName and 1 is returned.  If the - *	command doesn't exist then 0 is returned and *infoPtr isn't - *	modified. + *	If cmdName exists in interp, then *infoPtr is modified to hold + *	information about cmdName and 1 is returned. If the command doesn't + *	exist then 0 is returned and *infoPtr isn't modified.   *   * Side effects:   *	None. @@ -2325,20 +2803,17 @@ Tcl_SetCommandInfoFromToken( cmd, infoPtr )   */  int -Tcl_GetCommandInfo(interp, cmdName, infoPtr) -    Tcl_Interp *interp;			/* Interpreter in which to look -					 * for command. */ -    CONST char *cmdName;		/* Name of desired command. */ -    Tcl_CmdInfo *infoPtr;		/* Where to store information about -					 * command. */ +Tcl_GetCommandInfo( +    Tcl_Interp *interp,		/* Interpreter in which to look for +				 * command. */ +    const char *cmdName,	/* Name of desired command. */ +    Tcl_CmdInfo *infoPtr)	/* Where to store information about +				 * command. */  {      Tcl_Command cmd; -    cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, -            /*flags*/ 0); - -    return Tcl_GetCommandInfoFromToken( cmd, infoPtr ); - +    cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); +    return Tcl_GetCommandInfoFromToken(cmd, infoPtr);  }  /* @@ -2349,9 +2824,9 @@ Tcl_GetCommandInfo(interp, cmdName, infoPtr)   *	Returns various information about a Tcl command.   *   * Results: - *	Copies information from the command identified by 'cmd' into - *	a caller-supplied structure and returns 1.  If the 'cmd' is - *	NULL, leaves the structure untouched and returns 0. + *	Copies information from the command identified by 'cmd' into a + *	caller-supplied structure and returns 1. If the 'cmd' is NULL, leaves + *	the structure untouched and returns 0.   *   * Side effects:   *	None. @@ -2360,14 +2835,13 @@ Tcl_GetCommandInfo(interp, cmdName, infoPtr)   */  int -Tcl_GetCommandInfoFromToken( cmd, infoPtr ) -    Tcl_Command cmd; -    Tcl_CmdInfo* infoPtr; +Tcl_GetCommandInfoFromToken( +    Tcl_Command cmd, +    Tcl_CmdInfo *infoPtr)  { +    Command *cmdPtr;		/* Internal representation of the command */ -    Command* cmdPtr;		/* Internal representation of the command */ - -    if ( cmd == (Tcl_Command) NULL ) { +    if (cmd == NULL) {  	return 0;      } @@ -2388,7 +2862,6 @@ Tcl_GetCommandInfoFromToken( cmd, infoPtr )      infoPtr->namespacePtr = (Tcl_Namespace *) cmdPtr->nsPtr;      return 1; -  }  /* @@ -2396,9 +2869,8 @@ Tcl_GetCommandInfoFromToken( cmd, infoPtr )   *   * Tcl_GetCommandName --   * - *	Given a token returned by Tcl_CreateCommand, this procedure - *	returns the current name of the command (which may have changed - *	due to renaming). + *	Given a token returned by Tcl_CreateCommand, this function returns the + *	current name of the command (which may have changed due to renaming).   *   * Results:   *	The return value is the name of the given command. @@ -2409,25 +2881,25 @@ Tcl_GetCommandInfoFromToken( cmd, infoPtr )   *----------------------------------------------------------------------   */ -CONST char * -Tcl_GetCommandName(interp, command) -    Tcl_Interp *interp;		/* Interpreter containing the command. */ -    Tcl_Command command;	/* Token for command returned by a previous -				 * call to Tcl_CreateCommand. The command -				 * must not have been deleted. */ +const char * +Tcl_GetCommandName( +    Tcl_Interp *interp,		/* Interpreter containing the command. */ +    Tcl_Command command)	/* Token for command returned by a previous +				 * call to Tcl_CreateCommand. The command must +				 * not have been deleted. */  {      Command *cmdPtr = (Command *) command;      if ((cmdPtr == NULL) || (cmdPtr->hPtr == NULL)) { -  	/*  	 * This should only happen if command was "created" after the -	 * interpreter began to be deleted, so there isn't really any -	 * command. Just return an empty string. +	 * interpreter began to be deleted, so there isn't really any command. +	 * Just return an empty string.  	 */  	return "";      } +      return Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr);  } @@ -2436,28 +2908,28 @@ Tcl_GetCommandName(interp, command)   *   * Tcl_GetCommandFullName --   * - *	Given a token returned by, e.g., Tcl_CreateCommand or - *	Tcl_FindCommand, this procedure appends to an object the command's - *	full name, qualified by a sequence of parent namespace names. The - *	command's fully-qualified name may have changed due to renaming. + *	Given a token returned by, e.g., Tcl_CreateCommand or Tcl_FindCommand, + *	this function appends to an object the command's full name, qualified + *	by a sequence of parent namespace names. The command's fully-qualified + *	name may have changed due to renaming.   *   * Results:   *	None.   *   * Side effects:   *	The command's fully-qualified name is appended to the string - *	representation of objPtr.  + *	representation of objPtr.   *   *----------------------------------------------------------------------   */  void -Tcl_GetCommandFullName(interp, command, objPtr) -    Tcl_Interp *interp;		/* Interpreter containing the command. */ -    Tcl_Command command;	/* Token for command returned by a previous -				 * call to Tcl_CreateCommand. The command -				 * must not have been deleted. */ -    Tcl_Obj *objPtr;		/* Points to the object onto which the +Tcl_GetCommandFullName( +    Tcl_Interp *interp,		/* Interpreter containing the command. */ +    Tcl_Command command,	/* Token for command returned by a previous +				 * call to Tcl_CreateCommand. The command must +				 * not have been deleted. */ +    Tcl_Obj *objPtr)		/* Points to the object onto which the  				 * command's full name is appended. */  { @@ -2480,7 +2952,7 @@ Tcl_GetCommandFullName(interp, command, objPtr)  	if (cmdPtr->hPtr != NULL) {  	    name = Tcl_GetHashKey(cmdPtr->hPtr->tablePtr, cmdPtr->hPtr);  	    Tcl_AppendToObj(objPtr, name, -1); -	}  +	}      }  } @@ -2492,31 +2964,29 @@ Tcl_GetCommandFullName(interp, command, objPtr)   *	Remove the given command from the given interpreter.   *   * Results: - *	0 is returned if the command was deleted successfully. - *	-1 is returned if there didn't exist a command by that name. + *	0 is returned if the command was deleted successfully. -1 is returned + *	if there didn't exist a command by that name.   *   * Side effects: - *	cmdName will no longer be recognized as a valid command for - *	interp. + *	cmdName will no longer be recognized as a valid command for interp.   *   *----------------------------------------------------------------------   */  int -Tcl_DeleteCommand(interp, cmdName) -    Tcl_Interp *interp;		/* Token for command interpreter (returned -				 * by a previous Tcl_CreateInterp call). */ -    CONST char *cmdName;	/* Name of command to remove. */ +Tcl_DeleteCommand( +    Tcl_Interp *interp,		/* Token for command interpreter (returned by +				 * a previous Tcl_CreateInterp call). */ +    const char *cmdName)	/* Name of command to remove. */  {      Tcl_Command cmd;      /* -     *  Find the desired command and delete it. +     * Find the desired command and delete it.       */ -    cmd = Tcl_FindCommand(interp, cmdName, (Tcl_Namespace *) NULL, -            /*flags*/ 0); -    if (cmd == (Tcl_Command) NULL) { +    cmd = Tcl_FindCommand(interp, cmdName, NULL, /*flags*/ 0); +    if (cmd == NULL) {  	return -1;      }      return Tcl_DeleteCommandFromToken(interp, cmd); @@ -2527,26 +2997,26 @@ Tcl_DeleteCommand(interp, cmdName)   *   * Tcl_DeleteCommandFromToken --   * - *	Removes the given command from the given interpreter. This procedure - *	resembles Tcl_DeleteCommand, but takes a Tcl_Command token instead - *	of a command name for efficiency. + *	Removes the given command from the given interpreter. This function + *	resembles Tcl_DeleteCommand, but takes a Tcl_Command token instead of + *	a command name for efficiency.   *   * Results: - *	0 is returned if the command was deleted successfully. - *	-1 is returned if there didn't exist a command by that name. + *	0 is returned if the command was deleted successfully. -1 is returned + *	if there didn't exist a command by that name.   *   * Side effects: - *	The command specified by "cmd" will no longer be recognized as a - *	valid command for "interp". + *	The command specified by "cmd" will no longer be recognized as a valid + *	command for "interp".   *   *----------------------------------------------------------------------   */  int -Tcl_DeleteCommandFromToken(interp, cmd) -    Tcl_Interp *interp;		/* Token for command interpreter returned by -				 * a previous call to Tcl_CreateInterp. */ -    Tcl_Command cmd;            /* Token for command to delete. */ +Tcl_DeleteCommandFromToken( +    Tcl_Interp *interp,		/* Token for command interpreter returned by a +				 * previous call to Tcl_CreateInterp. */ +    Tcl_Command cmd)		/* Token for command to delete. */  {      Interp *iPtr = (Interp *) interp;      Command *cmdPtr = (Command *) cmd; @@ -2554,73 +3024,91 @@ Tcl_DeleteCommandFromToken(interp, cmd)      Tcl_Command importCmd;      /* -     * The code here is tricky.  We can't delete the hash table entry -     * before invoking the deletion callback because there are cases -     * where the deletion callback needs to invoke the command (e.g. -     * object systems such as OTcl). However, this means that the -     * callback could try to delete or rename the command. The deleted -     * flag allows us to detect these cases and skip nested deletes. +     * Bump the command epoch counter. This will invalidate all cached +     * references that point to this command. +     */ + +    cmdPtr->cmdEpoch++; + +    /* +     * The code here is tricky. We can't delete the hash table entry before +     * invoking the deletion callback because there are cases where the +     * deletion callback needs to invoke the command (e.g. object systems such +     * as OTcl). However, this means that the callback could try to delete or +     * rename the command. The deleted flag allows us to detect these cases +     * and skip nested deletes.       */      if (cmdPtr->flags & CMD_IS_DELETED) {  	/* -	 * Another deletion is already in progress.  Remove the hash -	 * table entry now, but don't invoke a callback or free the -	 * command structure. +	 * Another deletion is already in progress. Remove the hash table +	 * entry now, but don't invoke a callback or free the command +	 * structure. Take care to only remove the hash entry if it has not +	 * already been removed; otherwise if we manage to hit this function +	 * three times, everything goes up in smoke. [Bug 1220058]  	 */ -        Tcl_DeleteHashEntry(cmdPtr->hPtr); -	cmdPtr->hPtr = NULL; +	if (cmdPtr->hPtr != NULL) { +	    Tcl_DeleteHashEntry(cmdPtr->hPtr); +	    cmdPtr->hPtr = NULL; +	}  	return 0;      } -    /*  -     * We must delete this command, even though both traces and -     * delete procs may try to avoid this (renaming the command etc). -     * Also traces and delete procs may try to delete the command -     * themsevles.  This flag declares that a delete is in progress -     * and that recursive deletes should be ignored. -     */ -    cmdPtr->flags |= CMD_IS_DELETED; -      /* -     * Bump the command epoch counter. This will invalidate all cached -     * references that point to this command. +     * We must delete this command, even though both traces and delete procs +     * may try to avoid this (renaming the command etc). Also traces and +     * delete procs may try to delete the command themsevles. This flag +     * declares that a delete is in progress and that recursive deletes should +     * be ignored.       */ -     -    cmdPtr->cmdEpoch++; + +    cmdPtr->flags |= CMD_IS_DELETED;      /* -     * Call trace procedures for the command being deleted. Then delete -     * its traces.  +     * Call trace functions for the command being deleted. Then delete its +     * traces.       */      if (cmdPtr->tracePtr != NULL) {  	CommandTrace *tracePtr;  	CallCommandTraces(iPtr,cmdPtr,NULL,NULL,TCL_TRACE_DELETE); -	/* Now delete these traces */ + +	/* +	 * Now delete these traces. +	 */ +  	tracePtr = cmdPtr->tracePtr;  	while (tracePtr != NULL) {  	    CommandTrace *nextPtr = tracePtr->nextPtr; +  	    if ((--tracePtr->refCount) <= 0) { -		ckfree((char*)tracePtr); +		ckfree(tracePtr);  	    }  	    tracePtr = nextPtr;  	}  	cmdPtr->tracePtr = NULL;      } -     + +    /* +     * The list of command exported from the namespace might have changed. +     * However, we do not need to recompute this just yet; next time we need +     * the info will be soon enough. +     */ + +    TclInvalidateNsCmdLookup(cmdPtr->nsPtr); +      /* -     * If the command being deleted has a compile procedure, increment the -     * interpreter's compileEpoch to invalidate its compiled code. This -     * makes sure that we don't later try to execute old code compiled with -     * command-specific (i.e., inline) bytecodes for the now-deleted -     * command. This field is checked in Tcl_EvalObj and ObjInterpProc, and -     * code whose compilation epoch doesn't match is recompiled. +     * If the command being deleted has a compile function, increment the +     * interpreter's compileEpoch to invalidate its compiled code. This makes +     * sure that we don't later try to execute old code compiled with +     * command-specific (i.e., inline) bytecodes for the now-deleted command. +     * This field is checked in Tcl_EvalObj and ObjInterpProc, and code whose +     * compilation epoch doesn't match is recompiled.       */      if (cmdPtr->compileProc != NULL) { -        iPtr->compileEpoch++; +	iPtr->compileEpoch++;      }      if (cmdPtr->deleteProc != NULL) { @@ -2629,19 +3117,17 @@ Tcl_DeleteCommandFromToken(interp, cmd)  	 * 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() macro and you are now trying to deallocate -	 * this memory with free() instead of ckfree(). You should -	 * pass a pointer to your own method that calls ckfree(). +	 * cmdPtr->deleteProc is a pointer to the function free(), the most +	 * likely cause is that your extension allocated memory for the +	 * clientData argument to Tcl_CreateObjCommand with the ckalloc() +	 * macro and you are now trying to deallocate this memory with free() +	 * instead of ckfree(). You should pass a pointer to your own method +	 * that calls ckfree().  	 */ -	(*cmdPtr->deleteProc)(cmdPtr->deleteData); +	cmdPtr->deleteProc(cmdPtr->deleteData);      }      /* @@ -2649,80 +3135,96 @@ Tcl_DeleteCommandFromToken(interp, cmd)       * commands were created that refer back to this command. Delete these       * imported commands now.       */ - -    for (refPtr = cmdPtr->importRefPtr;  refPtr != NULL; -            refPtr = nextRefPtr) { -	nextRefPtr = refPtr->nextPtr; -	importCmd = (Tcl_Command) refPtr->importedCmdPtr; -        Tcl_DeleteCommandFromToken(interp, importCmd); +    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); +	}      }      /* -     * Don't use hPtr to delete the hash entry here, because it's -     * possible that the deletion callback renamed the command. -     * Instead, use cmdPtr->hptr, and make sure that no-one else -     * has already deleted the hash entry. +     * Don't use hPtr to delete the hash entry here, because it's possible +     * that the deletion callback renamed the command. Instead, use +     * cmdPtr->hptr, and make sure that no-one else has already deleted the +     * hash entry.       */      if (cmdPtr->hPtr != NULL) {  	Tcl_DeleteHashEntry(cmdPtr->hPtr); +	cmdPtr->hPtr = NULL;      }      /* -     * Mark the Command structure as no longer valid. This allows -     * TclExecuteByteCode to recognize when a Command has logically been -     * deleted and a pointer to this Command structure cached in a CmdName -     * object is invalid. TclExecuteByteCode will look up the command again -     * in the interpreter's command hashtable. +     * 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.       */      cmdPtr->objProc = NULL;      /* -     * Now free the Command structure, unless there is another reference to -     * it from a CmdName Tcl object in some ByteCode code sequence. In that -     * case, delay the cleanup until all references are either discarded -     * (when a ByteCode is freed) or replaced by a new reference (when a -     * cached CmdName Command reference is found to be invalid and -     * TclExecuteByteCode looks up the command in the command hashtable). +     * Now free the Command structure, unless there is another reference to it +     * from a CmdName Tcl object in some ByteCode code sequence. In that case, +     * delay the cleanup until all references are either discarded (when a +     * ByteCode is freed) or replaced by a new reference (when a cached +     * CmdName Command reference is found to be invalid and +     * TclNRExecuteByteCode looks up the command in the command hashtable).       */ -     -    TclCleanupCommand(cmdPtr); + +    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(iPtr, cmdPtr, oldName, newName, flags) -    Interp *iPtr;		/* Interpreter containing command. */ -    Command *cmdPtr;		/* Command whose traces are to be -				 * invoked. */ -    CONST char *oldName;        /* Command's old name, or NULL if we -                                 * must get the name from cmdPtr */ -    CONST char *newName;        /* Command's new name, or NULL if -                                 * the command is not being renamed */ -    int flags;			/* Flags indicating the type of traces -				 * to trigger, either TCL_TRACE_DELETE -				 * or TCL_TRACE_RENAME. */ +CallCommandTraces( +    Interp *iPtr,		/* Interpreter containing command. */ +    Command *cmdPtr,		/* Command whose traces are to be invoked. */ +    const char *oldName,	/* Command's old name, or NULL if we must get +				 * the name from cmdPtr */ +    const char *newName,	/* Command's new name, or NULL if the command +				 * is not being renamed */ +    int flags)			/* Flags indicating the type of traces to +				 * trigger, either TCL_TRACE_DELETE or +				 * TCL_TRACE_RENAME. */  {      register CommandTrace *tracePtr;      ActiveCommandTrace active;      char *result;      Tcl_Obj *oldNamePtr = NULL; -    int mask = (TCL_TRACE_DELETE | TCL_TRACE_RENAME);	/* Safety */ - -    flags &= mask; +    Tcl_InterpState state = NULL;      if (cmdPtr->flags & CMD_TRACE_ACTIVE) { -	/*  -	 * While a rename trace is active, we will not process any more -	 * rename traces; while a delete trace is active we will never -	 * reach here -- because Tcl_DeleteCommandFromToken checks for the -	 * condition (cmdPtr->flags & CMD_IS_DELETED) and returns immediately -	 * when a command deletion is in progress.  For all other traces, -	 * delete traces will not be invoked but a call to TraceCommandProc -	 * will ensure that tracePtr->clientData is freed whenever the -	 * command "oldName" is deleted. +	/* +	 * While a rename trace is active, we will not process any more rename +	 * traces; while a delete trace is active we will never reach here - +	 * because Tcl_DeleteCommandFromToken checks for the condition +	 * (cmdPtr->flags & CMD_IS_DELETED) and returns immediately when a +	 * command deletion is in progress. For all other traces, delete +	 * traces will not be invoked but a call to TraceCommandProc will +	 * ensure that tracePtr->clientData is freed whenever the command +	 * "oldName" is deleted.  	 */ +  	if (cmdPtr->flags & TCL_TRACE_RENAME) {  	    flags &= ~TCL_TRACE_RENAME;  	} @@ -2732,7 +3234,7 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)      }      cmdPtr->flags |= CMD_TRACE_ACTIVE;      cmdPtr->refCount++; -     +      result = NULL;      active.nextPtr = iPtr->activeCmdTracePtr;      active.reverseScan = 0; @@ -2742,37 +3244,41 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)  	flags |= TCL_TRACE_DESTROYED;      }      active.cmdPtr = cmdPtr; -     -    Tcl_Preserve((ClientData) iPtr); -     -    for (tracePtr = cmdPtr->tracePtr; tracePtr != NULL; -	 tracePtr = active.nextTracePtr) { -	int traceFlags = (tracePtr->flags & mask); +    Tcl_Preserve(iPtr); + +    for (tracePtr = cmdPtr->tracePtr; tracePtr != NULL; +	    tracePtr = active.nextTracePtr) {  	active.nextTracePtr = tracePtr->nextPtr; -	if (!(traceFlags & flags)) { +	if (!(tracePtr->flags & flags)) {  	    continue;  	} -	cmdPtr->flags |= traceFlags; +	cmdPtr->flags |= tracePtr->flags;  	if (oldName == NULL) {  	    TclNewObj(oldNamePtr);  	    Tcl_IncrRefCount(oldNamePtr); -	    Tcl_GetCommandFullName((Tcl_Interp *) iPtr,  -	            (Tcl_Command) cmdPtr, oldNamePtr); +	    Tcl_GetCommandFullName((Tcl_Interp *) iPtr, +		    (Tcl_Command) cmdPtr, oldNamePtr);  	    oldName = TclGetString(oldNamePtr);  	}  	tracePtr->refCount++; -	(*tracePtr->traceProc)(tracePtr->clientData, -		(Tcl_Interp *) iPtr, oldName, newName, flags); -	cmdPtr->flags &= ~traceFlags; +	if (state == NULL) { +	    state = Tcl_SaveInterpState((Tcl_Interp *) iPtr, TCL_OK); +	} +	tracePtr->traceProc(tracePtr->clientData, (Tcl_Interp *) iPtr, +		oldName, newName, flags); +	cmdPtr->flags &= ~tracePtr->flags;  	if ((--tracePtr->refCount) <= 0) { -	    ckfree((char*)tracePtr); +	    ckfree(tracePtr);  	}      } +    if (state) { +	Tcl_RestoreInterpState((Tcl_Interp *) iPtr, state); +    } +      /* -     * If a new object was created to hold the full oldName, -     * free it now. +     * If a new object was created to hold the full oldName, free it now.       */      if (oldNamePtr != NULL) { @@ -2780,26 +3286,103 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)      }      /* -     * Restore the variable's flags, remove the record of our active -     * traces, and then return. +     * Restore the variable's flags, remove the record of our active traces, +     * and then return.       */      cmdPtr->flags &= ~CMD_TRACE_ACTIVE;      cmdPtr->refCount--;      iPtr->activeCmdTracePtr = active.nextPtr; -    Tcl_Release((ClientData) iPtr); +    Tcl_Release(iPtr);      return result;  }  /*   *----------------------------------------------------------------------   * + * 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). + * + * Results: + *	The value given for the code argument. + * + * Side effects: + *	Transfers a message from the cancelation message to the interpreter. + * + *---------------------------------------------------------------------- + */ + +static int +CancelEvalProc( +    ClientData clientData,	/* Interp to cancel the script in progress. */ +    Tcl_Interp *interp,		/* Ignored */ +    int code)			/* Current return code from command. */ +{ +    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 slave +	     * interpreters belonging to this one. +	     */ + +	    TclSetSlaveCancelFlags((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); +    } + +    return code; +} + +/* + *---------------------------------------------------------------------- + *   * TclCleanupCommand --   * - *	This procedure frees up a Command structure unless it is still + *	This function frees up a Command structure unless it is still   *	referenced from an interpreter's command hashtable or from a CmdName   *	Tcl object representing the name of a command in a ByteCode - *	instruction sequence.  + *	instruction sequence.   *   * Results:   *	None. @@ -2813,13 +3396,13 @@ CallCommandTraces(iPtr, cmdPtr, oldName, newName, flags)   */  void -TclCleanupCommand(cmdPtr) -    register Command *cmdPtr;	/* Points to the Command structure to +TclCleanupCommand( +    register Command *cmdPtr)	/* Points to the Command structure to  				 * be freed. */  {      cmdPtr->refCount--;      if (cmdPtr->refCount <= 0) { -	ckfree((char *) cmdPtr); +	ckfree(cmdPtr);      }  } @@ -2828,18 +3411,17 @@ TclCleanupCommand(cmdPtr)   *   * Tcl_CreateMathFunc --   * - *	Creates a new math function for expressions in a given - *	interpreter. + *	Creates a new math function for expressions in a given interpreter.   *   * Results:   *	None.   *   * Side effects: - *	The function defined by "name" is created or redefined. If the - *	function already exists then its definition is replaced; this - *	includes the builtin functions. Redefining a builtin function forces - *	all existing code to be invalidated since that code may be compiled - *	using an instruction specific to the replaced function. In addition, + *	The Tcl function defined by "name" is created or redefined. If the + *	function already exists then its definition is replaced; this includes + *	the builtin functions. Redefining a builtin function forces all + *	existing code to be invalidated since that code may be compiled using + *	an instruction specific to the replaced function. In addition,   *	redefioning a non-builtin function will force existing code to be   *	invalidated if the number of arguments has changed.   * @@ -2847,65 +3429,203 @@ TclCleanupCommand(cmdPtr)   */  void -Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData) -    Tcl_Interp *interp;			/* Interpreter in which function is -					 * to be available. */ -    CONST char *name;			/* Name of function (e.g. "sin"). */ -    int numArgs;			/* Nnumber of arguments required by -					 * function. */ -    Tcl_ValueType *argTypes;		/* Array of types acceptable for -					 * each argument. */ -    Tcl_MathProc *proc;			/* Procedure that implements the -					 * math function. */ -    ClientData clientData;		/* Additional value to pass to the -					 * function. */ +Tcl_CreateMathFunc( +    Tcl_Interp *interp,		/* Interpreter in which function is to be +				 * available. */ +    const char *name,		/* Name of function (e.g. "sin"). */ +    int numArgs,		/* Nnumber of arguments required by +				 * function. */ +    Tcl_ValueType *argTypes,	/* Array of types acceptable for each +				 * argument. */ +    Tcl_MathProc *proc,		/* C function that implements the math +				 * function. */ +    ClientData clientData)	/* Additional value to pass to the +				 * function. */  { -    Interp *iPtr = (Interp *) interp; -    Tcl_HashEntry *hPtr; -    MathFunc *mathFuncPtr; -    int new, i; +    Tcl_DString bigName; +    OldMathFuncData *data = ckalloc(sizeof(OldMathFuncData)); + +    data->proc = proc; +    data->numArgs = numArgs; +    data->argTypes = 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, name, -1); + +    Tcl_CreateObjCommand(interp, Tcl_DStringValue(&bigName), +	    OldMathFuncProc, data, OldMathFuncDeleteProc); +    Tcl_DStringFree(&bigName); +} + +/* + *---------------------------------------------------------------------- + * + * OldMathFuncProc -- + * + *	Dispatch to a math function created with Tcl_CreateMathFunc + * + * Results: + *	Returns a standard Tcl result. + * + * Side effects: + *	Whatever the math function does. + * + *---------------------------------------------------------------------- + */ -    hPtr = Tcl_CreateHashEntry(&iPtr->mathFuncTable, name, &new); -    if (new) { -	Tcl_SetHashValue(hPtr, ckalloc(sizeof(MathFunc))); +static int +OldMathFuncProc( +    ClientData clientData,	/* Ponter to OldMathFuncData describing the +				 * function being called */ +    Tcl_Interp *interp,		/* Tcl interpreter */ +    int objc,			/* Actual parameter count */ +    Tcl_Obj *const *objv)	/* Parameter vector */ +{ +    Tcl_Obj *valuePtr; +    OldMathFuncData *dataPtr = clientData; +    Tcl_Value funcResult, *args; +    int result; +    int j, k; +    double d; + +    /* +     * Check argument count. +     */ + +    if (objc != dataPtr->numArgs + 1) { +	MathFuncWrongNumArgs(interp, dataPtr->numArgs+1, objc, objv); +	return TCL_ERROR;      } -    mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); -    if (!new) {	 -	if (mathFuncPtr->builtinFuncIndex >= 0) { -	    /* -	     * We are redefining a builtin math function. Invalidate the -             * interpreter's existing code by incrementing its -             * compileEpoch member. This field is checked in Tcl_EvalObj -             * and ObjInterpProc, and code whose compilation epoch doesn't -             * match is recompiled. Newly compiled code will no longer -             * treat the function as builtin. -	     */ +    /* +     * Convert arguments from Tcl_Obj's to Tcl_Value's. +     */ -	    iPtr->compileEpoch++; -	} else { +    args = ckalloc(dataPtr->numArgs * sizeof(Tcl_Value)); +    for (j = 1, k = 0; j < objc; ++j, ++k) { +	/* TODO: Convert to TclGetNumberFromObj? */ +	valuePtr = objv[j]; +	result = Tcl_GetDoubleFromObj(NULL, valuePtr, &d); +#ifdef ACCEPT_NAN +	if ((result != TCL_OK) && (valuePtr->typePtr == &tclDoubleType)) { +	    d = valuePtr->internalRep.doubleValue; +	    result = TCL_OK; +	} +#endif +	if (result != TCL_OK) {  	    /* -	     * A non-builtin function is being redefined. We must invalidate -             * existing code if the number of arguments has changed. This -	     * is because existing code was compiled assuming that number. +	     * We have a non-numeric argument.  	     */ -	    if (numArgs != mathFuncPtr->numArgs) { -		iPtr->compileEpoch++; +	    Tcl_SetObjResult(interp, Tcl_NewStringObj( +		    "argument to math function didn't have numeric value", +		    -1)); +	    TclCheckBadOctal(interp, Tcl_GetString(valuePtr)); +	    ckfree(args); +	    return TCL_ERROR; +	} + +	/* +	 * Copy the object's numeric value to the argument record, converting +	 * it if necessary. +	 * +	 * NOTE: no bignum support; use the new mathfunc interface for that. +	 */ + +	args[k].type = dataPtr->argTypes[k]; +	switch (args[k].type) { +	case TCL_EITHER: +	    if (Tcl_GetLongFromObj(NULL, valuePtr, &args[k].intValue) +		    == TCL_OK) { +		args[k].type = TCL_INT; +		break; +	    } +	    if (Tcl_GetWideIntFromObj(interp, valuePtr, &args[k].wideValue) +		    == TCL_OK) { +		args[k].type = TCL_WIDE_INT; +		break; +	    } +	    args[k].type = TCL_DOUBLE; +	    /* FALLTHROUGH */ + +	case TCL_DOUBLE: +	    args[k].doubleValue = d; +	    break; +	case TCL_INT: +	    if (ExprIntFunc(NULL, interp, 2, &objv[j-1]) != TCL_OK) { +		ckfree(args); +		return TCL_ERROR;  	    } +	    valuePtr = Tcl_GetObjResult(interp); +	    Tcl_GetLongFromObj(NULL, valuePtr, &args[k].intValue); +	    Tcl_ResetResult(interp); +	    break; +	case TCL_WIDE_INT: +	    if (ExprWideFunc(NULL, interp, 2, &objv[j-1]) != TCL_OK) { +		ckfree(args); +		return TCL_ERROR; +	    } +	    valuePtr = Tcl_GetObjResult(interp); +	    Tcl_GetWideIntFromObj(NULL, valuePtr, &args[k].wideValue); +	    Tcl_ResetResult(interp); +	    break;  	}      } -     -    mathFuncPtr->builtinFuncIndex = -1;	/* can't be a builtin function */ -    if (numArgs > MAX_MATH_ARGS) { -	numArgs = MAX_MATH_ARGS; + +    /* +     * Call the function. +     */ + +    errno = 0; +    result = dataPtr->proc(dataPtr->clientData, interp, args, &funcResult); +    ckfree(args); +    if (result != TCL_OK) { +	return result;      } -    mathFuncPtr->numArgs = numArgs; -    for (i = 0;  i < numArgs;  i++) { -	mathFuncPtr->argTypes[i] = argTypes[i]; + +    /* +     * Return the result of the call. +     */ + +    if (funcResult.type == TCL_INT) { +	TclNewLongObj(valuePtr, funcResult.intValue); +    } else if (funcResult.type == TCL_WIDE_INT) { +	valuePtr = Tcl_NewWideIntObj(funcResult.wideValue); +    } else { +	return CheckDoubleResult(interp, funcResult.doubleValue);      } -    mathFuncPtr->proc = proc; -    mathFuncPtr->clientData = clientData; +    Tcl_SetObjResult(interp, valuePtr); +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * OldMathFuncDeleteProc -- + * + *	Cleans up after deleting a math function registered with + *	Tcl_CreateMathFunc + * + * Results: + *	None. + * + * Side effects: + *	Frees allocated memory. + * + *---------------------------------------------------------------------- + */ + +static void +OldMathFuncDeleteProc( +    ClientData clientData) +{ +    OldMathFuncData *dataPtr = clientData; + +    ckfree(dataPtr->argTypes); +    ckfree(dataPtr);  }  /* @@ -2917,64 +3637,77 @@ Tcl_CreateMathFunc(interp, name, numArgs, argTypes, proc, clientData)   *	interpreter.   *   * Results: - *	TCL_OK if it succeeds, TCL_ERROR else (leaving an error message - *	in the interpreter result if that happens.) + *	TCL_OK if it succeeds, TCL_ERROR else (leaving an error message in the + *	interpreter result if that happens.)   *   * Side effects: - *	If this function succeeds, the variables pointed to by the - *	numArgsPtr and argTypePtr arguments will be updated to detail the - *	arguments allowed by the function.  The variable pointed to by the - *	procPtr argument will be set to NULL if the function is a builtin - *	function, and will be set to the address of the C function used to - *	implement the math function otherwise (in which case the variable - *	pointed to by the clientDataPtr argument will also be updated.) + *	If this function succeeds, the variables pointed to by the numArgsPtr + *	and argTypePtr arguments will be updated to detail the arguments + *	allowed by the function. The variable pointed to by the procPtr + *	argument will be set to NULL if the function is a builtin function, + *	and will be set to the address of the C function used to implement the + *	math function otherwise (in which case the variable pointed to by the + *	clientDataPtr argument will also be updated.)   *   *----------------------------------------------------------------------   */  int -Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr, -		    clientDataPtr) -    Tcl_Interp *interp; -    CONST char *name; -    int *numArgsPtr; -    Tcl_ValueType **argTypesPtr; -    Tcl_MathProc **procPtr; -    ClientData *clientDataPtr; +Tcl_GetMathFuncInfo( +    Tcl_Interp *interp, +    const char *name, +    int *numArgsPtr, +    Tcl_ValueType **argTypesPtr, +    Tcl_MathProc **procPtr, +    ClientData *clientDataPtr)  { -    Interp *iPtr = (Interp *) interp; -    Tcl_HashEntry *hPtr; -    MathFunc *mathFuncPtr; -    Tcl_ValueType *argTypes; -    int i,numArgs; +    Tcl_Obj *cmdNameObj; +    Command *cmdPtr; -    hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, name); -    if (hPtr == NULL) { -        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -                "math function \"", name, "\" not known in this interpreter", -		(char *) NULL); +    /* +     * Get the command that implements the math function. +     */ + +    TclNewLiteralStringObj(cmdNameObj, "tcl::mathfunc::"); +    Tcl_AppendToObj(cmdNameObj, name, -1); +    Tcl_IncrRefCount(cmdNameObj); +    cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, cmdNameObj); +    Tcl_DecrRefCount(cmdNameObj); + +    /* +     * Report unknown functions. +     */ + +    if (cmdPtr == NULL) { +        Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "unknown math function \"%s\"", name)); +	Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "MATHFUNC", name, NULL); +	*numArgsPtr = -1; +	*argTypesPtr = NULL; +	*procPtr = NULL; +	*clientDataPtr = NULL;  	return TCL_ERROR;      } -    mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr); -    *numArgsPtr = numArgs = mathFuncPtr->numArgs; -    if (numArgs == 0) { -	/* Avoid doing zero-sized allocs... */ -	numArgs = 1; -    } -    *argTypesPtr = argTypes = -	(Tcl_ValueType *)ckalloc(numArgs * sizeof(Tcl_ValueType)); -    for (i = 0; i < mathFuncPtr->numArgs; i++) { -	argTypes[i] = mathFuncPtr->argTypes[i]; -    } +    /* +     * Retrieve function info for user defined functions; return dummy +     * information for builtins. +     */ -    if (mathFuncPtr->builtinFuncIndex == -1) { -	*procPtr = (Tcl_MathProc *) NULL; +    if (cmdPtr->objProc == &OldMathFuncProc) { +	OldMathFuncData *dataPtr = cmdPtr->clientData; + +	*procPtr = dataPtr->proc; +	*numArgsPtr = dataPtr->numArgs; +	*argTypesPtr = dataPtr->argTypes; +	*clientDataPtr = dataPtr->clientData;      } else { -	*procPtr = mathFuncPtr->proc; -	*clientDataPtr = mathFuncPtr->clientData; +	*procPtr = NULL; +	*numArgsPtr = -1; +	*argTypesPtr = NULL; +	*procPtr = NULL; +	*clientDataPtr = NULL;      } -      return TCL_OK;  } @@ -2987,9 +3720,9 @@ Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr,   *	interpreter.   *   * Results: - *	A pointer to a Tcl_Obj structure with a reference count of zero, - *	or NULL in the case of an error (in which case a suitable error - *	message will be left in the interpreter result.) + *	A pointer to a Tcl_Obj structure with a reference count of zero, or + *	NULL in the case of an error (in which case a suitable error message + *	will be left in the interpreter result.)   *   * Side effects:   *	None. @@ -2998,28 +3731,33 @@ Tcl_GetMathFuncInfo(interp, name, numArgsPtr, argTypesPtr, procPtr,   */  Tcl_Obj * -Tcl_ListMathFuncs(interp, pattern) -    Tcl_Interp *interp; -    CONST char *pattern; +Tcl_ListMathFuncs( +    Tcl_Interp *interp, +    const char *pattern)  { -    Interp *iPtr = (Interp *) interp; -    Tcl_Obj *resultList = Tcl_NewObj(); -    register Tcl_HashEntry *hPtr; -    Tcl_HashSearch hSearch; -    CONST char *name; - -    for (hPtr = Tcl_FirstHashEntry(&iPtr->mathFuncTable, &hSearch); -	 hPtr != NULL; hPtr = Tcl_NextHashEntry(&hSearch)) { -        name = Tcl_GetHashKey(&iPtr->mathFuncTable, hPtr); -	if ((pattern == NULL || Tcl_StringMatch(name, pattern)) && -	    /* I don't expect this to fail, but... */ -	    Tcl_ListObjAppendElement(interp, resultList, -				     Tcl_NewStringObj(name,-1)) != TCL_OK) { -	    Tcl_DecrRefCount(resultList); -	    return NULL; -	} +    Tcl_Obj *script = Tcl_NewStringObj("::info functions ", -1); +    Tcl_Obj *result; +    Tcl_InterpState state; + +    if (pattern) { +	Tcl_Obj *patternObj = Tcl_NewStringObj(pattern, -1); +	Tcl_Obj *arg = Tcl_NewListObj(1, &patternObj); + +	Tcl_AppendObjToObj(script, arg); +	Tcl_DecrRefCount(arg);	/* Should tear down patternObj too */ +    } + +    state = Tcl_SaveInterpState(interp, TCL_OK); +    Tcl_IncrRefCount(script); +    if (TCL_OK == Tcl_EvalObjEx(interp, script, 0)) { +	result = Tcl_DuplicateObj(Tcl_GetObjResult(interp)); +    } else { +	result = Tcl_NewObj();      } -    return resultList; +    Tcl_DecrRefCount(script); +    Tcl_RestoreInterpState(interp, state); + +    return result;  }  /* @@ -3027,13 +3765,12 @@ Tcl_ListMathFuncs(interp, pattern)   *   * TclInterpReady --   * - *	Check if an interpreter is ready to eval commands or scripts,  - *      i.e., if it was not deleted and if the nesting level is not  - *      too high. + *	Check if an interpreter is ready to eval commands or scripts, i.e., if + *	it was not deleted and if the nesting level is not too high.   *   * Results: - *	The return value is TCL_OK if it the interpreter is ready,  - *      TCL_ERROR otherwise. + *	The return value is TCL_OK if it the interpreter is ready, TCL_ERROR + *	otherwise.   *   * Side effects:   *	The interpreters object and string results are cleared. @@ -3041,15 +3778,15 @@ Tcl_ListMathFuncs(interp, pattern)   *----------------------------------------------------------------------   */ -int  -TclInterpReady(interp) -    Tcl_Interp *interp; +int +TclInterpReady( +    Tcl_Interp *interp)  {      register Interp *iPtr = (Interp *) interp;      /* -     * Reset both the interpreter's string and object results and clear  -     * out any previous error information.  +     * Reset both the interpreter's string and object results and clear out +     * any previous error information.       */      Tcl_ResetResult(interp); @@ -3057,667 +3794,1050 @@ TclInterpReady(interp)      /*       * If the interpreter has been deleted, return an error.       */ -     +      if (iPtr->flags & DELETED) { -	Tcl_ResetResult(interp); -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "attempt to call eval in deleted interpreter", -1); -	Tcl_SetErrorCode(interp, "CORE", "IDELETE", -	        "attempt to call eval in deleted interpreter", -		(char *) NULL); +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"attempt to call eval in deleted interpreter", -1)); +	Tcl_SetErrorCode(interp, "TCL", "IDELETE", +		"attempt to call eval in deleted interpreter", NULL); +	return TCL_ERROR; +    } + +    if (iPtr->execEnvPtr->rewind) {  	return TCL_ERROR;      }      /* -     * Check depth of nested calls to Tcl_Eval:  if this gets too large, -     * it's probably because of an infinite loop somewhere. +     * Make sure the script being evaluated (if any) has not been canceled.       */ -    if (((iPtr->numLevels) > iPtr->maxNestingDepth)  -	    || (TclpCheckStackSpace() == 0)) { -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -		"too many nested evaluations (infinite loop?)", -1);  +    if (TclCanceled(iPtr) && +	    (TCL_OK != Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG))) {  	return TCL_ERROR;      } -    return TCL_OK; +    /* +     * Check depth of nested calls to Tcl_Eval: if this gets too large, it's +     * probably because of an infinite loop somewhere. +     */ + +    if (((iPtr->numLevels) <= iPtr->maxNestingDepth)) { +	return TCL_OK; +    } + +    Tcl_SetObjResult(interp, Tcl_NewStringObj( +	    "too many nested evaluations (infinite loop?)", -1)); +    Tcl_SetErrorCode(interp, "TCL", "LIMIT", "STACK", NULL); +    return TCL_ERROR;  }  /*   *----------------------------------------------------------------------   * - * TclEvalObjvInternal -- + * TclResetCancellation --   * - *	This procedure evaluates a Tcl command that has already been - *	parsed into words, with one Tcl_Obj holding each word. The caller - *      is responsible for managing the iPtr->numLevels. + *	Reset the script cancellation flags if the nesting level + *	(iPtr->numLevels) for the interp is zero or argument force is + *	non-zero.   *   * Results: - *	The return value is a standard Tcl completion code such as - *	TCL_OK or TCL_ERROR.  A result or error message is left in - *	interp's result.  If an error occurs, this procedure does - *	NOT add any information to the errorInfo variable. + *	A standard Tcl result.   *   * Side effects: - *	Depends on the command. + *	The script cancellation flags for the interp may be reset.   *   *----------------------------------------------------------------------   */  int -TclEvalObjvInternal(interp, objc, objv, command, length, flags) -    Tcl_Interp *interp;		/* Interpreter in which to evaluate the -				 * command.  Also used for error -				 * reporting. */ -    int objc;			/* Number of words in command. */ -    Tcl_Obj *CONST objv[];	/* An array of pointers to objects that are -				 * the words that make up the command. */ -    CONST char *command;	/* Points to the beginning of the string -				 * representation of the command; this -				 * is used for traces.  If the string -				 * representation of the command is -				 * unknown, an empty string should be -				 * supplied. If it is NULL, no traces will -				 * be called. */ -    int length;			/* Number of bytes in command; if -1, all -				 * 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 and TCL_EVAL_INVOKE are -				 * currently supported. */ - +TclResetCancellation( +    Tcl_Interp *interp, +    int force)  { -    Command *cmdPtr; -    Interp *iPtr = (Interp *) interp; -    Tcl_Obj **newObjv; -    int i; -    CallFrame *savedVarFramePtr;	/* Saves old copy of iPtr->varFramePtr -					 * in case TCL_EVAL_GLOBAL was set. */ -    int code = TCL_OK; -    int traceCode = TCL_OK; -    int checkTraces = 1; -    Namespace *savedNsPtr = NULL; +    register Interp *iPtr = (Interp *) interp; -    if (TclInterpReady(interp) == TCL_ERROR) { +    if (iPtr == NULL) {  	return TCL_ERROR;      } -    if (objc == 0) { -	return TCL_OK; +    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 master + *	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) +{ +    register Interp *iPtr = (Interp *) interp;      /* -     * If any execution traces rename or delete the current command, -     * we may need (at most) two passes here. +     * Has the current script in progress for this interpreter been canceled +     * or is the stack being unwound due to the previous script cancellation?       */ -    savedVarFramePtr = iPtr->varFramePtr; -    while (1) { -     -	/* Configure evaluation context to match the requested flags */ -	if (flags & TCL_EVAL_GLOBAL) { -	    iPtr->varFramePtr = NULL; -	} else if ((flags & TCL_EVAL_INVOKE) && iPtr->varFramePtr) { -	    savedNsPtr = iPtr->varFramePtr->nsPtr; -	    iPtr->varFramePtr->nsPtr = iPtr->globalNsPtr; -	} -	 -        /* -         * Find the procedure to execute this command. If there isn't one, -         * then see if there is a command "unknown".  If so, create a new -         * word array with "unknown" as the first word and the original -         * command words as arguments.  Then call ourselves recursively -         * to execute it. -         */ -        cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]); -        if (cmdPtr == NULL) { -	    newObjv = (Tcl_Obj **) ckalloc((unsigned) -		((objc + 1) * sizeof (Tcl_Obj *))); -	    for (i = objc-1; i >= 0; i--) { -	        newObjv[i+1] = objv[i]; -	    } -	    newObjv[0] = Tcl_NewStringObj("::unknown", -1); -	    Tcl_IncrRefCount(newObjv[0]); -	    cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, newObjv[0]); -	    if (cmdPtr == NULL) { -	        Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		    "invalid command name \"", Tcl_GetString(objv[0]), "\"", -		    (char *) NULL); -	        code = TCL_ERROR; -	    } else { -	        iPtr->numLevels++; -	        code = TclEvalObjvInternal(interp, objc+1, newObjv, -			command, length, 0); -	        iPtr->numLevels--; -	    } -	    Tcl_DecrRefCount(newObjv[0]); -	    ckfree((char *) newObjv); -	    if (savedNsPtr) { -		iPtr->varFramePtr->nsPtr = savedNsPtr; -	    } -	    goto done; -        } -	if (savedNsPtr) { -	    iPtr->varFramePtr->nsPtr = savedNsPtr; -	} -     -        /* -         * Call trace procedures if needed. -         */ -        if ((checkTraces) && (command != NULL)) { -            int cmdEpoch = cmdPtr->cmdEpoch; -	    int newEpoch; -	     -	    cmdPtr->refCount++; -            /*  -             * If the first set of traces modifies/deletes the command or -             * any existing traces, then the set checkTraces to 0 and -             * go through this while loop one more time. -             */ -            if (iPtr->tracePtr != NULL && traceCode == TCL_OK) { -                traceCode = TclCheckInterpTraces(interp, command, length, -                               cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); -            } -            if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES)  -		    && (traceCode == TCL_OK)) { -                traceCode = TclCheckExecutionTraces(interp, command, length, -                               cmdPtr, code, TCL_TRACE_ENTER_EXEC, objc, objv); -            } -	    newEpoch = cmdPtr->cmdEpoch; -	    TclCleanupCommand(cmdPtr); -            if (cmdEpoch != newEpoch) { -                /* The command has been modified in some way */ -                checkTraces = 0; -                continue; -            } -        } -        break; +    if (!TclCanceled(iPtr)) { +        return TCL_OK;      } -    if (TCL_DTRACE_CMD_ARGS_ENABLED()) { -	char *a[10]; -	int i = 0; +    /* +     * 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. +     */ -	while (i < 10) { -	    a[i] = i < objc ? TclGetString(objv[i]) : NULL; i++; -	} -	TCL_DTRACE_CMD_ARGS(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], -		a[8], a[9]); -    } +    iPtr->flags &= ~CANCELED;      /* -     * Finally, invoke the command's Tcl_ObjCmdProc. +     * 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.       */ -    cmdPtr->refCount++; -    iPtr->cmdCount++; -    if ( code == TCL_OK && traceCode == TCL_OK) { -	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); -	} -    } -    if (Tcl_AsyncReady()) { -	code = Tcl_AsyncInvoke(interp, code); + +    if ((flags & TCL_CANCEL_UNWIND) && !(iPtr->flags & TCL_CANCEL_UNWIND)) { +        return TCL_OK;      }      /* -     * Call 'leave' command traces +     * If the TCL_LEAVE_ERR_MSG flags bit is set, place an error in the +     * interp's result; otherwise, we leave it alone.       */ -    if (!(cmdPtr->flags & CMD_IS_DELETED)) { -	int saveErrFlags = iPtr->flags  -		& (ERR_IN_PROGRESS | ERR_ALREADY_LOGGED | ERROR_CODE_SET); -        if ((cmdPtr->flags & CMD_HAS_EXEC_TRACES) && (traceCode == TCL_OK)) { -            traceCode = TclCheckExecutionTraces (interp, command, length, -                   cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv); -        } -        if (iPtr->tracePtr != NULL && traceCode == TCL_OK) { -            traceCode = TclCheckInterpTraces(interp, command, length, -                   cmdPtr, code, TCL_TRACE_LEAVE_EXEC, objc, objv); + +    if (flags & TCL_LEAVE_ERR_MSG) { +        const char *id, *message = NULL; +        int length; + +        /* +         * Setup errorCode variables so that we can differentiate between +         * being canceled and unwound. +         */ + +        if (iPtr->asyncCancelMsg != NULL) { +            message = Tcl_GetStringFromObj(iPtr->asyncCancelMsg, &length); +        } else { +            length = 0;          } -	if (traceCode == TCL_OK) { -	    iPtr->flags |= saveErrFlags; -	} -    } -    TclCleanupCommand(cmdPtr); -    /* -     * If one of the trace invocation resulted in error, then  -     * change the result code accordingly. Note, that the -     * interp->result should already be set correctly by the -     * call to TraceExecutionProc.   -     */ +        if (iPtr->flags & TCL_CANCEL_UNWIND) { +            id = "IUNWIND"; +            if (length == 0) { +                message = "eval unwound"; +            } +        } else { +            id = "ICANCEL"; +            if (length == 0) { +                message = "eval canceled"; +            } +        } -    if (traceCode != TCL_OK) { -	code = traceCode; +        Tcl_SetObjResult(interp, Tcl_NewStringObj(message, -1)); +        Tcl_SetErrorCode(interp, "TCL", "CANCEL", id, message, NULL);      } -     +      /* -     * If the interpreter has a non-empty string result, the result -     * object is either empty or stale because some procedure set -     * interp->result directly. If so, move the string result to the -     * result object, then reset the string result. +     * 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.       */ -     -    if (*(iPtr->result) != 0) { -	(void) Tcl_GetObjResult(interp); -    } -    if (TCL_DTRACE_CMD_RESULT_ENABLED()) { -	Tcl_Obj *r; - -	r = Tcl_GetObjResult(interp); -	TCL_DTRACE_CMD_RESULT(TclGetString(objv[0]), code, TclGetString(r), r); -    } - -    done: -    iPtr->varFramePtr = savedVarFramePtr; -    return code; +    return TCL_ERROR;  }  /*   *----------------------------------------------------------------------   * - * Tcl_EvalObjv -- + * Tcl_CancelEval --   * - *	This procedure evaluates a Tcl command that has already been - *	parsed into words, with one Tcl_Obj holding each word. + *	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.  A result or error message is left in - *	interp's result. + *	The return value is a standard Tcl completion code such as TCL_OK or + *	TCL_ERROR. Since the interp may belong to a different thread, no error + *	message can be left in the interp's result.   *   * Side effects: - *	Depends on the command. + *	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_EvalObjv(interp, objc, objv, flags) -    Tcl_Interp *interp;		/* Interpreter in which to evaluate the -				 * command.  Also used for error -				 * reporting. */ -    int objc;			/* Number of words in command. */ -    Tcl_Obj *CONST objv[];	/* An array of pointers to objects that are -				 * the words that make up the command. */ -    int flags;			/* Collection of OR-ed bits that control -				 * the evaluation of the script.  Only -				 * TCL_EVAL_GLOBAL and TCL_EVAL_INVOKE -				 * are  currently supported. */ +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. */ +    ClientData 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. */  { -    Interp *iPtr = (Interp *)interp; -    Trace *tracePtr; -    Tcl_DString cmdBuf; -    char *cmdString = "";	/* A command string is only necessary for -				 * command traces or error logs; it will be -				 * generated to replace this default value if -				 * necessary. */ -    int cmdLen = 0;		/* a non-zero value indicates that a command -				 * string was generated. */ -    int code = TCL_OK; -    int i; -    int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); +    Tcl_HashEntry *hPtr; +    CancelInfo *cancelInfo; +    int code = TCL_ERROR; +    const char *result; -    for (tracePtr = iPtr->tracePtr; tracePtr; tracePtr = tracePtr->nextPtr) { -	if ((tracePtr->level == 0) || (iPtr->numLevels <= tracePtr->level)) { -	    /* -	     * The command may be needed for an execution trace.  Generate a -	     * command string. -	     */ -	     -	    Tcl_DStringInit(&cmdBuf); -	    for (i = 0; i < objc; i++) { -		Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i])); -	    } -	    cmdString = Tcl_DStringValue(&cmdBuf); -	    cmdLen = Tcl_DStringLength(&cmdBuf); -	    break; -	} +    if (interp == NULL) { +	return TCL_ERROR;      } -    iPtr->numLevels++; -    code = TclEvalObjvInternal(interp, objc, objv, cmdString, cmdLen, flags); -    iPtr->numLevels--; +    Tcl_MutexLock(&cancelLock); +    if (cancelTableInitialized != 1) { +	/* +	 * No CancelInfo hash table (Tcl_CreateInterp has never been called?) +	 */ -    /* -     * If we are again at the top level, process any unusual  -     * return code returned by the evaluated code.  -     */ -	 -    if (iPtr->numLevels == 0) { -	if (code == TCL_RETURN) { -	    code = TclUpdateReturnInfo(iPtr); -	} -	if ((code != TCL_OK) && (code != TCL_ERROR)  -	    && !allowExceptions) { -	    ProcessUnexpectedResult(interp, code); -	    code = TCL_ERROR; -	} +	goto done;      } -	     -    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 if it was not done previously. +    hPtr = Tcl_FindHashEntry(&cancelTable, (char *) interp); +    if (hPtr == NULL) { +	/* +	 * No CancelInfo record for this interpreter.  	 */ -	if (cmdLen == 0) { -	    Tcl_DStringInit(&cmdBuf); -	    for (i = 0; i < objc; i++) { -		Tcl_DStringAppendElement(&cmdBuf, Tcl_GetString(objv[i])); -	    } -	    cmdString = Tcl_DStringValue(&cmdBuf); -	    cmdLen = Tcl_DStringLength(&cmdBuf); -	} -	Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen); +	goto done;      } +    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 (cmdLen != 0) { -	Tcl_DStringFree(&cmdBuf); +    if (resultObjPtr != NULL) { +	result = Tcl_GetStringFromObj(resultObjPtr, &cancelInfo->length); +	cancelInfo->result = ckrealloc(cancelInfo->result,cancelInfo->length); +	memcpy(cancelInfo->result, result, (size_t) cancelInfo->length); +	TclDecrRefCount(resultObjPtr);	/* Discard their result object. */ +    } else { +	cancelInfo->result = NULL; +	cancelInfo->length = 0;      } +    cancelInfo->clientData = clientData; +    cancelInfo->flags = flags; +    Tcl_AsyncMark(cancelInfo->async); +    code = TCL_OK; + +  done: +    Tcl_MutexUnlock(&cancelLock);      return code;  }  /*   *----------------------------------------------------------------------   * - * Tcl_LogCommandInfo -- + * Tcl_InterpActive --   * - *	This procedure is invoked after an error occurs in an interpreter. - *	It adds information to the "errorInfo" variable to describe the - *	command that was being executed when the error occurred. + *	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; +} + +/* + *---------------------------------------------------------------------- + * + * 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: - *	Information about the command is added to errorInfo and the - *	line number stored internally in the interpreter is set.  If this - *	is the first call to this procedure or Tcl_AddObjErrorInfo since - *	an error occurred, then old information in errorInfo is - *	deleted. + *	Always pushes a callback. Other side effects depend on the command.   *   *----------------------------------------------------------------------   */ -void -Tcl_LogCommandInfo(interp, script, command, length) -    Tcl_Interp *interp;		/* Interpreter in which to log information. */ -    CONST char *script;		/* First character in script containing -				 * command (must be <= command). */ -    CONST char *command;	/* First character in command that -				 * generated the error. */ -    int length;			/* Number of bytes in command (-1 means -				 * use all bytes up to first null byte). */ -{ -    char buffer[200]; -    register CONST char *p; -    char *ellipsis = ""; +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, 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. */ +    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, 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; -    if (iPtr->flags & ERR_ALREADY_LOGGED) { +    /* +     * data[1] stores a marker for use by tailcalls; it will be set to 1 by +     * command redirectors (imports, alias, ensembles) so that tailcalls +     * finishes the source command and not just the target. +     */ + +    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( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Command *cmdPtr = NULL, *preCmdPtr = data[0]; +    int flags = PTR2INT(data[1]); +    int objc = PTR2INT(data[2]); +    Tcl_Obj **objv = data[3]; +    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); +    } + +    if (TCL_OK != TclInterpReady(interp)) { +	return TCL_ERROR; +    } + +    if (objc == 0) { +	return TCL_OK; +    } + +    if (TclLimitExceeded(iPtr->limit)) { +	return TCL_ERROR; +    } + +    /* +     * Configure evaluation context to match the requested flags. +     */ + +    if (iPtr->lookupNsPtr) { +  	/* -	 * Someone else has already logged error information for this -	 * command; we shouldn't add anything more. +	 * 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?  	 */ -	return; +	lookupNsPtr = iPtr->lookupNsPtr; +	iPtr->lookupNsPtr = NULL; +    } else if (flags & TCL_EVAL_INVOKE) { +	lookupNsPtr = iPtr->globalNsPtr; +    } else { + +	/* +	 * TCL_EVAL_INVOKE was not set: clear rewrite rules +	 */ + +	iPtr->ensembleRewrite.sourceObjs = NULL; + +	if (flags & TCL_EVAL_GLOBAL) { +	    TEOV_SwitchVarFrame(interp); +	    lookupNsPtr = iPtr->globalNsPtr; +	}      }      /* -     * Compute the line number where the error occurred. +     * Lookup the Command to dispatch.       */ -    iPtr->errorLine = 1; -    for (p = script; p != command; p++) { -	if (*p == '\n') { -	    iPtr->errorLine++; +    reresolve: +    assert(cmdPtr == NULL); +    if (preCmdPtr) { +	/* Caller gave it to us */ +	if (!(preCmdPtr->flags & CMD_IS_DELETED)) { +	    /* So long as it exists, use it. */ +	    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. +	     */ +	    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		    "attempt to invoke a deleted command")); +	    Tcl_SetErrorCode(interp, "TCL", "EVAL", "DELETEDCOMMAND", NULL); +	    return TCL_ERROR; +	} +    } +    if (cmdPtr == NULL) { +	cmdPtr = TEOV_LookupCmdFromObj(interp, objv[0], lookupNsPtr); +	if (!cmdPtr) { +	    return TEOV_NotFound(interp, objc, objv, lookupNsPtr); +	} +    } + +    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; +	    } + +	    /* +	     * If the enter traces made the resolved cmdPtr unusable, go +	     * back and resolve again, but next time don't run enter +	     * traces again. +	     */ + +	    if (cmdPtr == NULL) { +		enterTracesDone = 1; +		Tcl_DecrRefCount(commandPtr); +		goto reresolve; +	    }  	} + +	/*  +	 * 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( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Tcl_ObjCmdProc *objProc = data[0]; +    ClientData clientData = data[1]; +    int objc = PTR2INT(data[2]); +    Tcl_Obj **objv = data[3]; +    Interp *iPtr = (Interp *) interp; + +#ifdef USE_DTRACE +    if (TCL_DTRACE_CMD_ARGS_ENABLED()) { +	const char *a[10]; +	int i = 0; + +	while (i < 10) { +	    a[i] = i < objc ? TclGetString(objv[i]) : NULL; i++; +	} +	TCL_DTRACE_CMD_ARGS(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], +		a[8], a[9]); +    } +    if (TCL_DTRACE_CMD_INFO_ENABLED() && iPtr->cmdFramePtr) { +	Tcl_Obj *info = TclInfoFrame(interp, iPtr->cmdFramePtr); +	const char *a[6]; 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]); +	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. */ +{ +    Interp *iPtr = (Interp *) interp; +    NRE_callback *callbackPtr; +    Tcl_NRPostProc *procPtr; +      /* -     * Create an error message to add to errorInfo, including up to a -     * maximum number of characters of the command. +     * 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 (length < 0) { -	length = strlen(command); +    if (*(iPtr->result) != 0) { +	(void) Tcl_GetObjResult(interp);      } -    if (length > 150) { -	length = 150; -	ellipsis = "..."; + +    while (TOP_CB(interp) != rootPtr) { +	callbackPtr = TOP_CB(interp); +	procPtr = callbackPtr->procPtr; +	TOP_CB(interp) = callbackPtr->nextPtr; +	result = procPtr(callbackPtr->data, interp, result); +	TCLNR_FREE(interp, callbackPtr);      } -    while ( (command[length] & 0xC0) == 0x80 ) { -	/* -	 * Back up truncation point so that we don't truncate in the -	 * middle of a multi-byte character (in UTF-8) -	 */ -	length--; -	ellipsis = "..."; +    return result; +} + +static int +NRCommand( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; + +    iPtr->numLevels--; + +     /* +      * If there is a tailcall, schedule it +      */ +  +    if (data[1] && (data[1] != INT2PTR(1))) { +        TclNRAddCallback(interp, TclNRTailcallEval, data[1], NULL, NULL, NULL);      } -    if (!(iPtr->flags & ERR_IN_PROGRESS)) { -	sprintf(buffer, "\n    while executing\n\"%.*s%s\"", -		length, command, ellipsis); -    } else { -	sprintf(buffer, "\n    invoked from within\n\"%.*s%s\"", -		length, command, ellipsis); + +    /* 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);      } -    Tcl_AddObjErrorInfo(interp, buffer, -1); -    iPtr->flags &= ~ERR_ALREADY_LOGGED; +    if ((result == TCL_OK) && TclCanceled(iPtr)) { +	result = Tcl_Canceled(interp, TCL_LEAVE_ERR_MSG); +    } +    if (result == TCL_OK && TclLimitReady(iPtr->limit)) { +	result = Tcl_LimitCheck(interp); +    } + +    return result;  }  /*   *----------------------------------------------------------------------   * - * Tcl_EvalTokensStandard, EvalTokensStandard -- - * - *	Given an array of tokens parsed from a Tcl command (e.g., the - *	tokens that make up a word or the index for an array variable) - *	this procedure evaluates the tokens and concatenates their - *	values to form a single result value. - *  - * Results: - *	The return value is a standard Tcl completion code such as - *	TCL_OK or TCL_ERROR.  A result or error message is left in - *	interp's result. + * TEOV_Exception	 - + * TEOV_LookupCmdFromObj - + * TEOV_RunEnterTraces	 - + * TEOV_RunLeaveTraces	 - + * TEOV_NotFound	 -   * - * Side effects: - *	Depends on the array of tokens being evaled. + *	These are helper functions for Tcl_EvalObjv.   * - * TIP #280 : Keep public API, internally extended API.   *----------------------------------------------------------------------   */ -int -Tcl_EvalTokensStandard(interp, tokenPtr, count) -    Tcl_Interp *interp;		/* Interpreter in which to lookup -				 * variables, execute nested commands, -				 * and report errors. */ -    Tcl_Token *tokenPtr;	/* Pointer to first in an array of tokens -				 * to evaluate and concatenate. */ -    int count;			/* Number of tokens to consider at tokenPtr. -				 * Must be at least 1. */ +static void +TEOV_PushExceptionHandlers( +    Tcl_Interp *interp, +    int 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. +     */ + +    if (!(flags & TCL_EVAL_INVOKE)) { +	/* +	 * Error messages +	 */ + +	TclNRAddCallback(interp, TEOV_Error, INT2PTR(objc), +		(ClientData) objv, NULL, NULL); +    } + +    if (iPtr->numLevels == 1) { +	/* +	 * No CONTINUE or BREAK at level 0, manage RETURN +	 */ + +	TclNRAddCallback(interp, TEOV_Exception, INT2PTR(iPtr->evalFlags), +		NULL, NULL, NULL); +    } +} + +static void +TEOV_SwitchVarFrame( +    Tcl_Interp *interp)  { -#ifdef TCL_TIP280 -  return EvalTokensStandard (interp, tokenPtr, count, 1); +    Interp *iPtr = (Interp *) interp; + +    /* +     * Change the varFrame to be the rootVarFrame, and push a record to +     * restore things at the end. +     */ + +    TclNRAddCallback(interp, TEOV_RestoreVarFrame, iPtr->varFramePtr, NULL, +	    NULL, NULL); +    iPtr->varFramePtr = iPtr->rootFramePtr;  }  static int -EvalTokensStandard(interp, tokenPtr, count, line) -    Tcl_Interp *interp;		/* Interpreter in which to lookup -				 * variables, execute nested commands, -				 * and report errors. */ -    Tcl_Token *tokenPtr;	/* Pointer to first in an array of tokens -				 * to evaluate and concatenate. */ -    int count;			/* Number of tokens to consider at tokenPtr. -				 * Must be at least 1. */ -    int line;                   /* The line the script starts on. */ +TEOV_RestoreVarFrame( +    ClientData data[], +    Tcl_Interp *interp, +    int result)  { -#endif -    Tcl_Obj *resultPtr, *indexPtr, *valuePtr; -    char buffer[TCL_UTF_MAX]; -#ifdef TCL_MEM_DEBUG -#   define  MAX_VAR_CHARS 5 -#else -#   define  MAX_VAR_CHARS 30 -#endif -    char nameBuffer[MAX_VAR_CHARS+1]; -    char *varName, *index; -    CONST char *p = NULL;	/* Initialized to avoid compiler warning. */ -    int length, code; +    ((Interp *) interp)->varFramePtr = data[0]; +    return result; +} + +static int +TEOV_Exception( +    ClientData 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_ERROR) && !allowExceptions) { +	    ProcessUnexpectedResult(interp, result); +	    result = TCL_ERROR; +	} +    }      /* -     * The only tricky thing about this procedure is that it attempts to -     * avoid object creation and string copying whenever possible.  For -     * example, if the value is just a nested command, then use the -     * command's result object directly. +     * 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.       */ -    code = TCL_OK; -    resultPtr = NULL; -    Tcl_ResetResult(interp); -    for ( ; count > 0; count--, tokenPtr++) { -	valuePtr = NULL; +    TclUnsetCancelFlags(iPtr); +    return result; +} +static int +TEOV_Error( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    Tcl_Obj *listPtr; +    const char *cmdString; +    int cmdLen; +    int objc = PTR2INT(data[0]); +    Tcl_Obj **objv = data[1]; + +    if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)){  	/* -	 * The switch statement below computes the next value to be -	 * concat to the result, as either a range of text or an -	 * object. +	 * 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.  	 */ -	switch (tokenPtr->type) { -	    case TCL_TOKEN_TEXT: -		p = tokenPtr->start; -		length = tokenPtr->size; -		break; +	listPtr = Tcl_NewListObj(objc, objv); +	cmdString = Tcl_GetStringFromObj(listPtr, &cmdLen); +	Tcl_LogCommandInfo(interp, cmdString, cmdString, cmdLen); +	Tcl_DecrRefCount(listPtr); +    } +    iPtr->flags &= ~ERR_ALREADY_LOGGED; +    return result; +} -	    case TCL_TOKEN_BS: -		length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL, -			buffer); -		p = buffer; -		break; +static int +TEOV_NotFound( +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[], +    Namespace *lookupNsPtr) +{ +    Command * cmdPtr; +    Interp *iPtr = (Interp *) interp; +    int 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; -	    case TCL_TOKEN_COMMAND: { -		Interp *iPtr = (Interp *) interp; -		iPtr->numLevels++; -		code = TclInterpReady(interp); -		if (code == TCL_OK) { -#ifndef TCL_TIP280 -		    code = Tcl_EvalEx(interp, -			    tokenPtr->start+1, tokenPtr->size-2, 0); -#else -		    /* TIP #280: Transfer line information to nested command */ -		    code = EvalEx(interp, -			    tokenPtr->start+1, tokenPtr->size-2, 0, line); -#endif -		} -		iPtr->numLevels--; -		if (code != TCL_OK) { -		    goto done; -		} -		valuePtr = Tcl_GetObjResult(interp); -		break; -	    } +    currNsPtr = varFramePtr->nsPtr; +    if ((currNsPtr == NULL) || (currNsPtr->unknownHandlerPtr == NULL)) { +	currNsPtr = iPtr->globalNsPtr; +	if (currNsPtr == NULL) { +	    Tcl_Panic("Tcl_EvalObjv: NULL global namespace pointer"); +	} +    } -	    case TCL_TOKEN_VARIABLE: -		if (tokenPtr->numComponents == 1) { -		    indexPtr = NULL; -		    index = NULL; -		} else { -#ifndef TCL_TIP280 -		    code = Tcl_EvalTokensStandard(interp, tokenPtr+2, -			    tokenPtr->numComponents - 1); -#else -		    /* TIP #280: Transfer line information to nested command */ -		    code = EvalTokensStandard(interp, tokenPtr+2, -			    tokenPtr->numComponents - 1, line); -#endif -		    if (code != TCL_OK) { -			goto done; -		    } -		    indexPtr = Tcl_GetObjResult(interp); -		    Tcl_IncrRefCount(indexPtr); -		    index = Tcl_GetString(indexPtr); -		} +    /* +     * Check to see if the resolution namespace has lost its unknown handler. +     * If so, reset it to "::unknown". +     */ -		/* -		 * We have to make a copy of the variable name in order -		 * to have a null-terminated string.  We can't make a -		 * temporary modification to the script to null-terminate -		 * the name, because a trace callback might potentially -		 * reuse the script and be affected by the null character. -		 */ +    if (currNsPtr->unknownHandlerPtr == NULL) { +	TclNewLiteralStringObj(currNsPtr->unknownHandlerPtr, "::unknown"); +	Tcl_IncrRefCount(currNsPtr->unknownHandlerPtr); +    } -		if (tokenPtr[1].size <= MAX_VAR_CHARS) { -		    varName = nameBuffer; -		} else { -		    varName = ckalloc((unsigned) (tokenPtr[1].size + 1)); -		} -		strncpy(varName, tokenPtr[1].start, (size_t) tokenPtr[1].size); -		varName[tokenPtr[1].size] = 0; -		valuePtr = Tcl_GetVar2Ex(interp, varName, index, -			TCL_LEAVE_ERR_MSG); -		if (varName != nameBuffer) { -		    ckfree(varName); -		} -		if (indexPtr != NULL) { -		    Tcl_DecrRefCount(indexPtr); -		} -		if (valuePtr == NULL) { -		    code = TCL_ERROR; -		    goto done; -		} -		count -= tokenPtr->numComponents; -		tokenPtr += tokenPtr->numComponents; -		break; +    /* +     * 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. +     */ -	    default: -		panic("unexpected token type in Tcl_EvalTokensStandard"); -	} +    Tcl_ListObjGetElements(NULL, currNsPtr->unknownHandlerPtr, +	    &handlerObjc, &handlerObjv); +    newObjc = objc + handlerObjc; +    newObjv = TclStackAlloc(interp, (int) sizeof(Tcl_Obj *) * newObjc); + +    /* +     * Copy command prefix from unknown handler and add on the real command's +     * full argument list. Note that we only use memcpy() once because we have +     * to increment the reference count of all the handler arguments anyway. +     */ + +    for (i = 0; i < handlerObjc; ++i) { +	newObjv[i] = handlerObjv[i]; +	Tcl_IncrRefCount(newObjv[i]); +    } +    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. +     */ + +    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]), NULL);  	/* -	 * If valuePtr isn't NULL, the next piece of text comes from that -	 * object; otherwise, take length bytes starting at p. +	 * Release any resources we locked and allocated during the handler +	 * call.  	 */ -	if (resultPtr == NULL) { -	    if (valuePtr != NULL) { -		resultPtr = valuePtr; -	    } else { -		resultPtr = Tcl_NewStringObj(p, length); -	    } -	    Tcl_IncrRefCount(resultPtr); -	} else { -	    if (Tcl_IsShared(resultPtr)) { -		Tcl_DecrRefCount(resultPtr); -		resultPtr = Tcl_DuplicateObj(resultPtr); -		Tcl_IncrRefCount(resultPtr); -	    } -	    if (valuePtr != NULL) { -		p = Tcl_GetStringFromObj(valuePtr, &length); -	    } -	    Tcl_AppendToObj(resultPtr, p, length); +	for (i = 0; i < handlerObjc; ++i) { +	    Tcl_DecrRefCount(newObjv[i]);  	} +	TclStackFree(interp, newObjv); +	return TCL_ERROR;      } -    if (resultPtr != NULL) { -	Tcl_SetObjResult(interp, resultPtr); -    } else { -	code = 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); +} -    done: -    if (resultPtr != NULL) { -	Tcl_DecrRefCount(resultPtr); +static int +TEOV_NotFoundCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    int objc = PTR2INT(data[0]); +    Tcl_Obj **objv = data[1]; +    Namespace *savedNsPtr = data[2]; + +    int i; + +    if (savedNsPtr) { +	iPtr->varFramePtr->nsPtr = savedNsPtr;      } -    return code; + +    /* +     * Release any resources we locked and allocated during the handler call. +     */ + +    for (i = 0; i < objc; ++i) { +	Tcl_DecrRefCount(objv[i]); +    } +    TclStackFree(interp, objv); + +    return result; +} + +static int +TEOV_RunEnterTraces( +    Tcl_Interp *interp, +    Command **cmdPtrPtr, +    Tcl_Obj *commandPtr, +    int objc, +    Tcl_Obj *const objv[]) +{ +    Interp *iPtr = (Interp *) interp; +    Command *cmdPtr = *cmdPtrPtr; +    int newEpoch, cmdEpoch = cmdPtr->cmdEpoch; +    int length, traceCode = TCL_OK; +    const char *command = Tcl_GetStringFromObj(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. +     */ + +    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); + +    if (traceCode != TCL_OK) { +	if (traceCode == TCL_ERROR) { +	    Tcl_Obj *info; + +	    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; +} + +static int +TEOV_RunLeaveTraces( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    int traceCode = TCL_OK; +    int objc = PTR2INT(data[0]); +    Tcl_Obj *commandPtr = data[1]; +    Command *cmdPtr = data[2]; +    Tcl_Obj **objv = data[3]; +    int length; +    const char *command = Tcl_GetStringFromObj(commandPtr, &length); + +    if (!(cmdPtr->flags & CMD_IS_DELETED)) { +	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); +	} +    } + +    /* +     * 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 (traceCode != TCL_OK) { +	if (traceCode == TCL_ERROR) { +	    Tcl_Obj *info; + +	    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; +	} +	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; +    } +    cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, namePtr); +    iPtr->varFramePtr->nsPtr = savedNsPtr; +    return cmdPtr; +} + +/* + *---------------------------------------------------------------------- + * + * Tcl_EvalTokensStandard -- + * + *	Given an array of tokens parsed from a Tcl command (e.g., the tokens + *	that make up a word or the index for an array variable) this function + *	evaluates the tokens and concatenates their values to form a single + *	result value. + * + * 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 array of tokens being evaled. + * + *---------------------------------------------------------------------- + */ + +int +Tcl_EvalTokensStandard( +    Tcl_Interp *interp,		/* Interpreter in which to lookup variables, +				 * execute nested commands, and report +				 * errors. */ +    Tcl_Token *tokenPtr,	/* Pointer to first in an array of tokens to +				 * evaluate and concatenate. */ +    int count)			/* Number of tokens to consider at tokenPtr. +				 * Must be at least 1. */ +{ +    return TclSubstTokens(interp, tokenPtr, count, /* numLeftPtr */ NULL, 1, +	    NULL, NULL);  }  /* @@ -3725,67 +4845,62 @@ EvalTokensStandard(interp, tokenPtr, count, line)   *   * Tcl_EvalTokens --   * - *	Given an array of tokens parsed from a Tcl command (e.g., the - *	tokens that make up a word or the index for an array variable) - *	this procedure evaluates the tokens and concatenates their - *	values to form a single result value. + *	Given an array of tokens parsed from a Tcl command (e.g., the tokens + *	that make up a word or the index for an array variable) this function + *	evaluates the tokens and concatenates their values to form a single + *	result value.   *   * Results: - *	The return value is a pointer to a newly allocated Tcl_Obj - *	containing the value of the array of tokens.  The reference - *	count of the returned object has been incremented.  If an error - *	occurs in evaluating the tokens then a NULL value is returned - *	and an error message is left in interp's result. + *	The return value is a pointer to a newly allocated Tcl_Obj containing + *	the value of the array of tokens. The reference count of the returned + *	object has been incremented. If an error occurs in evaluating the + *	tokens then a NULL value is returned and an error message is left in + *	interp's result.   *   * Side effects:   *	A new object is allocated to hold the result.   *   *----------------------------------------------------------------------   * - * This uses a non-standard return convention; its use is now deprecated. - * It is a wrapper for the new function Tcl_EvalTokensStandard, and is not  - * used in the core any longer. It is only kept for backward compatibility. + * This uses a non-standard return convention; its use is now deprecated. It + * is a wrapper for the new function Tcl_EvalTokensStandard, and is not used + * in the core any longer. It is only kept for backward compatibility.   */  Tcl_Obj * -Tcl_EvalTokens(interp, tokenPtr, count) -    Tcl_Interp *interp;		/* Interpreter in which to lookup -				 * variables, execute nested commands, -				 * and report errors. */ -    Tcl_Token *tokenPtr;	/* Pointer to first in an array of tokens -				 * to evaluate and concatenate. */ -    int count;			/* Number of tokens to consider at tokenPtr. +Tcl_EvalTokens( +    Tcl_Interp *interp,		/* Interpreter in which to lookup variables, +				 * execute nested commands, and report +				 * errors. */ +    Tcl_Token *tokenPtr,	/* Pointer to first in an array of tokens to +				 * evaluate and concatenate. */ +    int count)			/* Number of tokens to consider at tokenPtr.  				 * Must be at least 1. */  { -    int code;      Tcl_Obj *resPtr; -     -    code = Tcl_EvalTokensStandard(interp, tokenPtr, count); -    if (code == TCL_OK) { -	resPtr = Tcl_GetObjResult(interp); -	Tcl_IncrRefCount(resPtr); -	Tcl_ResetResult(interp); -	return resPtr; -    } else { + +    if (Tcl_EvalTokensStandard(interp, tokenPtr, count) != TCL_OK) {  	return NULL;      } +    resPtr = Tcl_GetObjResult(interp); +    Tcl_IncrRefCount(resPtr); +    Tcl_ResetResult(interp); +    return resPtr;  } -  /*   *----------------------------------------------------------------------   * - * Tcl_EvalEx, EvalEx -- + * Tcl_EvalEx, TclEvalEx --   * - *	This procedure evaluates a Tcl script without using the compiler - *	or byte-code interpreter.  It just parses the script, creates - *	values for each word of each command, then calls EvalObjv - *	to execute each command. + *	This function evaluates a Tcl script without using the compiler or + *	byte-code interpreter. It just parses the script, creates values for + *	each word of each command, then calls EvalObjv to execute each + *	command.   *   * Results: - *	The return value is a standard Tcl completion code such as - *	TCL_OK or TCL_ERROR.  A result or error message is left in - *	interp's result. + *	The return value is a standard Tcl completion code such as TCL_OK or + *	TCL_ERROR. A result or error message is left in interp's result.   *   * Side effects:   *	Depends on the script. @@ -3795,61 +4910,89 @@ Tcl_EvalTokens(interp, tokenPtr, count)   */  int -Tcl_EvalEx(interp, script, numBytes, flags) -    Tcl_Interp *interp;		/* Interpreter in which to evaluate the -				 * script.  Also used for error reporting. */ -    CONST char *script;		/* First character of script to evaluate. */ -    int numBytes;		/* Number of bytes in script.  If < 0, the +Tcl_EvalEx( +    Tcl_Interp *interp,		/* Interpreter in which to evaluate the +				 * script. Also used for error reporting. */ +    const char *script,		/* First character of script to evaluate. */ +    int numBytes,		/* Number of bytes in script. If < 0, the  				 * script consists of all bytes up to the  				 * first null character. */ -    int flags;			/* Collection of OR-ed bits that control -				 * the evaluation of the script.  Only -				 * TCL_EVAL_GLOBAL is currently -				 * supported. */ +    int flags)			/* Collection of OR-ed bits that control the +				 * evaluation of the script. Only +				 * TCL_EVAL_GLOBAL is currently supported. */  { -#ifdef TCL_TIP280 -  return EvalEx (interp, script, numBytes, flags, 1); +    return TclEvalEx(interp, script, numBytes, flags, 1, NULL, script);  } -static int -EvalEx(interp, script, numBytes, flags, line) -    Tcl_Interp *interp;		/* Interpreter in which to evaluate the -				 * script.  Also used for error reporting. */ -    CONST char *script;		/* First character of script to evaluate. */ -    int numBytes;		/* Number of bytes in script.  If < 0, the +int +TclEvalEx( +    Tcl_Interp *interp,		/* Interpreter in which to evaluate the +				 * script. Also used for error reporting. */ +    const char *script,		/* First character of script to evaluate. */ +    int numBytes,		/* Number of bytes in script. If < 0, the  				 * script consists of all bytes up to the -				 * first null character. */ -    int flags;			/* Collection of OR-ed bits that control -				 * the evaluation of the script.  Only -				 * TCL_EVAL_GLOBAL is currently -				 * supported. */ -    int line;                   /* The line the script starts on. */ +				 * first NUL character. */ +    int flags,			/* Collection of OR-ed bits that control the +				 * evaluation of the script. Only +				 * TCL_EVAL_GLOBAL is currently supported. */ +    int line,			/* The line the script starts on. */ +    int *clNextOuter,		/* Information about an outer context for */ +    const char *outerScript)	/* continuation line data. This is set only in +				 * TclSubstTokens(), to properly handle +				 * [...]-nested commands. The 'outerScript' +				 * refers to the most-outer script containing +				 * the embedded command, which is refered to +				 * by '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. */  { -#endif      Interp *iPtr = (Interp *) interp; -    CONST char *p, *next; -    Tcl_Parse parse; -#define NUM_STATIC_OBJS 20 -    Tcl_Obj *staticObjArray[NUM_STATIC_OBJS], **objv; +    const char *p, *next; +    const unsigned int minObjs = 20; +    Tcl_Obj **objv, **objvSpace; +    int *expand, *lines, *lineSpace;      Tcl_Token *tokenPtr; -    int code = TCL_OK; -    int i, commandLength, bytesLeft, nested; -    CallFrame *savedVarFramePtr;   /* Saves old copy of iPtr->varFramePtr -				    * in case TCL_EVAL_GLOBAL was set. */ +    int commandLength, bytesLeft, expandRequested, code = TCL_OK; +    CallFrame *savedVarFramePtr;/* Saves old copy of iPtr->varFramePtr in case +				 * TCL_EVAL_GLOBAL was set. */      int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); -     -    /* -     * The variables below keep track of how much state has been -     * allocated while evaluating the script, so that it can be freed -     * properly if an error occurs. -     */ - -    int gotParse = 0, objectsUsed = 0; - -#ifdef TCL_TIP280 -    /* TIP #280 Structures for tracking of command locations. */ -    CmdFrame eeFrame; -#endif +    int gotParse = 0; +    unsigned int i, objectsUsed = 0; +				/* These variables keep track of how much +				 * state has been allocated while evaluating +				 * the script, so that it can be freed +				 * properly if an error occurs. */ +    Tcl_Parse *parsePtr = TclStackAlloc(interp, sizeof(Tcl_Parse)); +    CmdFrame *eeFramePtr = TclStackAlloc(interp, sizeof(CmdFrame)); +    Tcl_Obj **stackObjArray = +	    TclStackAlloc(interp, minObjs * sizeof(Tcl_Obj *)); +    int *expandStack = TclStackAlloc(interp, minObjs * sizeof(int)); +    int *linesStack = TclStackAlloc(interp, minObjs * sizeof(int)); +				/* TIP #280 Structures for tracking of command +				 * locations. */ +    int *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. */ + +    if (iPtr->scriptCLLocPtr) { +	if (clNextOuter) { +	    clNext = clNextOuter; +	} else { +	    clNext = &iPtr->scriptCLLocPtr->loc[0]; +	} +    }      if (numBytes < 0) {  	numBytes = strlen(script); @@ -3858,176 +5001,239 @@ EvalEx(interp, script, numBytes, flags, line)      savedVarFramePtr = iPtr->varFramePtr;      if (flags & TCL_EVAL_GLOBAL) { -	iPtr->varFramePtr = NULL; +	iPtr->varFramePtr = iPtr->rootFramePtr;      }      /* -     * Each iteration through the following loop parses the next -     * command from the script and then executes it. +     * Each iteration through the following loop parses the next command from +     * the script and then executes it.       */ -    objv = staticObjArray; +    objv = objvSpace = stackObjArray; +    lines = lineSpace = linesStack; +    expand = expandStack;      p = script;      bytesLeft = numBytes; -    if (iPtr->evalFlags & TCL_BRACKET_TERM) { -	nested = 1; -    } else { -	nested = 0; -    } -#ifdef TCL_TIP280 -    /* TIP #280 Initialize tracking. Do not push on the frame stack yet. */      /* -     * We may cont. counting based on a specific context (CTX), or open a new -     * context, either for a sourced script, or 'eval'. For sourced files we -     * always have a path object, even if nothing was specified in the interp -     * itself. That makes code using it simpler as NULL checks can be left -     * out. Sourced file without path in the 'scriptFile' is possible during -     * Tcl initialization. +     * TIP #280 Initialize tracking. Do not push on the frame stack yet. +     * +     * We open a new context, either for a sourced script, or 'eval'. +     * For sourced files we always have a path object, even if nothing was +     * specified in the interp itself. That makes code using it simpler as +     * NULL checks can be left out. Sourced file without path in the +     * 'scriptFile' is possible during Tcl initialization.       */ -    if (iPtr->evalFlags & TCL_EVAL_CTX) { -        /* Path information comes out of the context. */ +    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; -        eeFrame.type           = TCL_LOCATION_SOURCE; -	eeFrame.data.eval.path = iPtr->invokeCmdFramePtr->data.eval.path; -	Tcl_IncrRefCount (eeFrame.data.eval.path); -    } else if (iPtr->evalFlags & TCL_EVAL_FILE) { -	/* Set up for a sourced file */ +    iPtr->cmdFramePtr = eeFramePtr; +    if (iPtr->evalFlags & TCL_EVAL_FILE) { +	/* +	 * Set up for a sourced file. +	 */ -        eeFrame.type = TCL_LOCATION_SOURCE; +	eeFramePtr->type = TCL_LOCATION_SOURCE;  	if (iPtr->scriptFile) { -	    /* Normalization here, to have the correct pwd. Should have +	    /* +	     * Normalization here, to have the correct pwd. Should have  	     * negligible impact on performance, as the norm should have been  	     * done already by the 'source' invoking us, and it caches the -	     * result +	     * result.  	     */ -	    Tcl_Obj* norm = Tcl_FSGetNormalizedPath (interp, iPtr->scriptFile); -	    if (!norm) { -		/* Error message in the interp result */ -		return TCL_ERROR; +	    Tcl_Obj *norm = Tcl_FSGetNormalizedPath(interp, iPtr->scriptFile); + +	    if (norm == NULL) { +		/* +		 * Error message in the interp result. +		 */ + +		code = TCL_ERROR; +		goto error;  	    } -	    eeFrame.data.eval.path = norm; +	    eeFramePtr->data.eval.path = norm;  	} else { -	    eeFrame.data.eval.path = Tcl_NewStringObj ("",-1); +	    TclNewLiteralStringObj(eeFramePtr->data.eval.path, "");  	} -	Tcl_IncrRefCount (eeFrame.data.eval.path); +	Tcl_IncrRefCount(eeFramePtr->data.eval.path);      } else { -	/* Set up for plain eval */ +	/* +	 * Set up for plain eval. +	 */ -        eeFrame.type           = TCL_LOCATION_EVAL; -	eeFrame.data.eval.path = NULL; +	eeFramePtr->type = TCL_LOCATION_EVAL; +	eeFramePtr->data.eval.path = NULL;      } -    eeFrame.level     = (iPtr->cmdFramePtr == NULL -			 ? 1 -			 : iPtr->cmdFramePtr->level + 1); -    eeFrame.framePtr  = iPtr->framePtr; -    eeFrame.nextPtr   = iPtr->cmdFramePtr; -    eeFrame.nline     = 0; -    eeFrame.line      = NULL; -#endif -      iPtr->evalFlags = 0;      do { -	if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse) -	        != TCL_OK) { +	if (Tcl_ParseCommand(interp, p, bytesLeft, 0, parsePtr) != TCL_OK) {  	    code = TCL_ERROR; -	    goto error; +	    Tcl_LogCommandInfo(interp, script, parsePtr->commandStart, +		    parsePtr->term + 1 - parsePtr->commandStart); +	    goto posterror;  	} -	gotParse = 1;  -	if (nested && parse.term == (script + numBytes)) { -	    /* -	     * A nested script can only terminate in ']'. If -	     * the parsing got terminated at the end of the script, -	     * there was no closing ']'.  Report the syntax error. -	     */ - -	    code = TCL_ERROR; -	    goto error; -	} - -#ifdef TCL_TIP280  	/*  	 * TIP #280 Track lines. The parser may have skipped text till it -	 * found the command we are now at. We have count the lines in this -	 * block. +	 * found the command we are now at. We have to count the lines in this +	 * block, and do not forget invisible continuation lines.  	 */ -	TclAdvanceLines (&line, p, parse.commandStart); -#endif +	TclAdvanceLines(&line, p, parsePtr->commandStart); +	TclAdvanceContinuations(&line, &clNext, +		parsePtr->commandStart - outerScript); -	if (parse.numWords > 0) { -#ifdef TCL_TIP280 +	gotParse = 1; +	if (parsePtr->numWords > 0) {  	    /*  	     * TIP #280. Track lines within the words of the current -	     * command. +	     * command. We use a separate pointer into the table of +	     * continuation line locations to not lose our position for the +	     * per-command parsing.  	     */ -	    int         wordLine  = line; -	    CONST char* wordStart = parse.commandStart; -#endif +	    int wordLine = line; +	    const char *wordStart = parsePtr->commandStart; +	    int *wordCLNext = clNext; +	    unsigned int objectsNeeded = 0; +	    unsigned int numWords = parsePtr->numWords;  	    /*  	     * Generate an array of objects for the words of the command.  	     */ -     -	    if (parse.numWords <= NUM_STATIC_OBJS) { -		objv = staticObjArray; -	    } else { -		objv = (Tcl_Obj **) ckalloc((unsigned) -		    (parse.numWords * sizeof (Tcl_Obj *))); -	    } - -#ifdef TCL_TIP280 -	    eeFrame.nline = parse.numWords; -	    eeFrame.line  = (int*) ckalloc((unsigned) -		  (parse.numWords * sizeof (int))); -#endif -	    for (objectsUsed = 0, tokenPtr = parse.tokenPtr; -		 objectsUsed < parse.numWords; -		 objectsUsed++, tokenPtr += (tokenPtr->numComponents + 1)) { -#ifndef TCL_TIP280 -		code = Tcl_EvalTokensStandard(interp, tokenPtr+1,  -		            tokenPtr->numComponents); -#else -	        /* -		 * TIP #280. Track lines to current word. Save the -		 * information on a per-word basis, signaling dynamic words as -		 * needed. Make the information available to the recursively -		 * called evaluator as well, including the type of context -		 * (source vs. eval). +	    if (numWords > minObjs) { +		expand =    ckalloc(numWords * sizeof(int)); +		objvSpace = ckalloc(numWords * sizeof(Tcl_Obj *)); +		lineSpace = 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) { +		/* +		 * TIP #280. Track lines to current word. Save the information +		 * on a per-word basis, signaling dynamic words as needed. +		 * Make the information available to the recursively called +		 * evaluator as well, including the type of context (source +		 * vs. eval).  		 */ -		TclAdvanceLines (&wordLine, wordStart, tokenPtr->start); +		TclAdvanceLines(&wordLine, wordStart, tokenPtr->start); +		TclAdvanceContinuations(&wordLine, &wordCLNext, +			tokenPtr->start - outerScript);  		wordStart = tokenPtr->start; -                eeFrame.line [objectsUsed] = (TclWordKnownAtCompileTime (tokenPtr) -					      ? wordLine -					      : -1); +		lines[objectsUsed] = TclWordKnownAtCompileTime(tokenPtr, NULL) +			? wordLine : -1; -	        if (eeFrame.type == TCL_LOCATION_SOURCE) { +		if (eeFramePtr->type == TCL_LOCATION_SOURCE) {  		    iPtr->evalFlags |= TCL_EVAL_FILE;  		} -		code = EvalTokensStandard(interp, tokenPtr+1,  -		            tokenPtr->numComponents, wordLine); +		code = TclSubstTokens(interp, tokenPtr+1, +			tokenPtr->numComponents, NULL, wordLine, +			wordCLNext, outerScript);  		iPtr->evalFlags = 0; -#endif -		if (code == TCL_OK) { -		    objv[objectsUsed] = Tcl_GetObjResult(interp); -		    Tcl_IncrRefCount(objv[objectsUsed]); +		if (code != TCL_OK) { +		    break; +		} +		objv[objectsUsed] = Tcl_GetObjResult(interp); +		Tcl_IncrRefCount(objv[objectsUsed]); +		if (tokenPtr->type == TCL_TOKEN_EXPAND_WORD) { +		    int numElements; + +		    code = TclListObjLength(interp, objv[objectsUsed], +			    &numElements); +		    if (code == TCL_ERROR) { +			/* +			 * Attempt to expand a non-list. +			 */ + +			Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( +				"\n    (expanding word %d)", objectsUsed)); +			Tcl_DecrRefCount(objv[objectsUsed]); +			break; +		    } +		    expandRequested = 1; +		    expand[objectsUsed] = 1; + +		    objectsNeeded += (numElements ? numElements : 1);  		} else { -		    goto error; +		    expand[objectsUsed] = 0; +		    objectsNeeded++;  		} + +		if (wordCLNext) { +		    TclContinuationsEnterDerived(objv[objectsUsed], +			    wordStart - outerScript, wordCLNext); +		} +	    } /* for loop */ +	    iPtr->cmdFramePtr = eeFramePtr; +	    if (code != TCL_OK) { +		goto error;  	    } -     +	    if (expandRequested) { +		/* +		 * Some word expansion was requested. Check for objv resize. +		 */ + +		Tcl_Obj **copy = objvSpace; +		int *lcopy = lineSpace; +		int wordIdx = numWords; +		int objIdx = objectsNeeded - 1; + +		if ((numWords > minObjs) || (objectsNeeded > minObjs)) { +		    objv = objvSpace = +			    ckalloc(objectsNeeded * sizeof(Tcl_Obj *)); +		    lines = lineSpace = ckalloc(objectsNeeded * sizeof(int)); +		} + +		objectsUsed = 0; +		while (wordIdx--) { +		    if (expand[wordIdx]) { +			int numElements; +			Tcl_Obj **elements, *temp = copy[wordIdx]; + +			Tcl_ListObjGetElements(NULL, temp, &numElements, +				&elements); +			objectsUsed += numElements; +			while (numElements--) { +			    lines[objIdx] = -1; +			    objv[objIdx--] = elements[numElements]; +			    Tcl_IncrRefCount(elements[numElements]); +			} +			Tcl_DecrRefCount(temp); +		    } else { +			lines[objIdx] = lcopy[wordIdx]; +			objv[objIdx--] = copy[wordIdx]; +			objectsUsed++; +		    } +		} +		objv += objIdx+1; + +		if (copy != stackObjArray) { +		    ckfree(copy); +		} +		if (lcopy != linesStack) { +		    ckfree(lcopy); +		} +	    } +  	    /*  	     * Execute the command and free the objects for its words.  	     * @@ -4038,29 +5244,28 @@ EvalEx(interp, script, numBytes, flags, line)  	     * have been executed.  	     */ -#ifdef TCL_TIP280 -	    eeFrame.cmd.str.cmd = parse.commandStart; -	    eeFrame.cmd.str.len = parse.commandSize; +	    eeFramePtr->cmd = parsePtr->commandStart; +	    eeFramePtr->len = parsePtr->commandSize; -	    if (parse.term == parse.commandStart + parse.commandSize - 1) { -		eeFrame.cmd.str.len --; +	    if (parsePtr->term == +		    parsePtr->commandStart + parsePtr->commandSize - 1) { +		eeFramePtr->len--;  	    } -	    TclArgumentEnter (interp, objv, objectsUsed, &eeFrame); -	    iPtr->cmdFramePtr = &eeFrame; -#endif -	    iPtr->numLevels++;     -	    code = TclEvalObjvInternal(interp, objectsUsed, objv,  -	            parse.commandStart, parse.commandSize, 0); -	    iPtr->numLevels--; -#ifdef TCL_TIP280 -	    iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; -	    TclArgumentRelease (interp, objv, objectsUsed); - -	    ckfree ((char*) eeFrame.line); -	    eeFrame.line  = NULL; -	    eeFrame.nline = 0; -#endif +	    eeFramePtr->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); + +	    eeFramePtr->line = NULL; +	    eeFramePtr->nline = 0; +	    if (eeFramePtr->cmdObj) { +		Tcl_DecrRefCount(eeFramePtr->cmdObj); +		eeFramePtr->cmdObj = NULL; +	    }  	    if (code != TCL_OK) {  		goto error; @@ -4069,9 +5274,21 @@ EvalEx(interp, script, numBytes, flags, line)  		Tcl_DecrRefCount(objv[i]);  	    }  	    objectsUsed = 0; -	    if (objv != staticObjArray) { -		ckfree((char *) objv); -		objv = staticObjArray; +	    if (objvSpace != stackObjArray) { +		ckfree(objvSpace); +		objvSpace = stackObjArray; +		ckfree(lineSpace); +		lineSpace = linesStack; +	    } + +	    /* +	     * Free expand separately since objvSpace could have been +	     * reallocated above. +	     */ + +	    if (expand != expandStack) { +		ckfree(expand); +		expand = expandStack;  	    }  	} @@ -4082,214 +5299,93 @@ EvalEx(interp, script, numBytes, flags, line)  	 * executed command.  	 */ -	next = parse.commandStart + parse.commandSize; +	next = parsePtr->commandStart + parsePtr->commandSize;  	bytesLeft -= next - p;  	p = next; -#ifdef TCL_TIP280 -	TclAdvanceLines (&line, parse.commandStart, p); -#endif -	Tcl_FreeParse(&parse); +	TclAdvanceLines(&line, parsePtr->commandStart, p); +	Tcl_FreeParse(parsePtr);  	gotParse = 0; -	if (nested && (*parse.term == ']')) { -	    /* -	     * We get here in the special case where the TCL_BRACKET_TERM -	     * flag was set in the interpreter and the latest parsed command -	     * was terminated by the matching close-bracket we seek. -	     * Return immediately. -	     */ - -	    iPtr->termOffset = (p - 1) - script; -	    iPtr->varFramePtr = savedVarFramePtr; -#ifndef TCL_TIP280 -	    return TCL_OK; -#else -	    code = TCL_OK; -	    goto cleanup_return; -#endif -	}      } while (bytesLeft > 0); - -    if (nested) { -	/* -	 * This nested script did not terminate in ']', it is an error. -	 */ -	 -	code = TCL_ERROR; -	goto error; -    } -     -    iPtr->termOffset = p - script;      iPtr->varFramePtr = savedVarFramePtr; -#ifndef TCL_TIP280 -    return TCL_OK; -#else      code = TCL_OK;      goto cleanup_return; -#endif -    error: +  error:      /* -     * Generate various pieces of error information, such as the line -     * number where the error occurred and information to add to the -     * errorInfo variable.  Then free resources that had been allocated -     * to the command. +     * Generate and log various pieces of error information.       */      if (iPtr->numLevels == 0) {  	if (code == TCL_RETURN) {  	    code = TclUpdateReturnInfo(iPtr);  	} -	if ((code != TCL_OK) && (code != TCL_ERROR)  -		&& !allowExceptions) { +	if ((code != TCL_OK) && (code != TCL_ERROR) && !allowExceptions) {  	    ProcessUnexpectedResult(interp, code);  	    code = TCL_ERROR;  	}      } -    if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {  -	commandLength = parse.commandSize; -	if (parse.term == parse.commandStart + commandLength - 1) { +    if ((code == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) { +	commandLength = parsePtr->commandSize; +	if (parsePtr->term == parsePtr->commandStart + commandLength - 1) {  	    /*  	     * The terminator character (such as ; or ]) of the command where  	     * the error occurred is the last character in the parsed command.  	     * Reduce the length by one so that the error message doesn't  	     * include the terminator character.  	     */ -	     +  	    commandLength -= 1;  	} -	Tcl_LogCommandInfo(interp, script, parse.commandStart, commandLength); +	Tcl_LogCommandInfo(interp, script, parsePtr->commandStart, +		commandLength);      } -     + posterror: +    iPtr->flags &= ~ERR_ALREADY_LOGGED; + +    /* +     * Then free resources that had been allocated to the command. +     */ +      for (i = 0; i < objectsUsed; i++) {  	Tcl_DecrRefCount(objv[i]);      }      if (gotParse) { -	Tcl_FreeParse(&parse); +	Tcl_FreeParse(parsePtr);      } -    if (objv != staticObjArray) { -	ckfree((char *) objv); +    if (objvSpace != stackObjArray) { +	ckfree(objvSpace); +	ckfree(lineSpace);      } -    iPtr->varFramePtr = savedVarFramePtr; - -    /* -     * All that's left to do before returning is to set iPtr->termOffset -     * to point past the end of the script we just evaluated. -     */ - -    next = parse.commandStart + parse.commandSize; -    bytesLeft -= next - p; -    p = next; - -    if (!nested) { -	iPtr->termOffset = p - script; -#ifndef TCL_TIP280 -	return code; -#else -	goto cleanup_return; -#endif +    if (expand != expandStack) { +	ckfree(expand);      } +    iPtr->varFramePtr = savedVarFramePtr; + cleanup_return:      /* -     * When we are nested (the TCL_BRACKET_TERM flag was set in the -     * interpreter), we must find the matching close-bracket to -     * end the script we are evaluating. -     * -     * When our return code is TCL_CONTINUE or TCL_RETURN, we want -     * to correctly set iPtr->termOffset to point to that matching -     * close-bracket so our caller can move to the part of the -     * string beyond the script we were asked to evaluate. -     * So we try to parse past the rest of the commands. +     * TIP #280. Release the local CmdFrame, and its contents.       */ -    next = NULL; -    while (bytesLeft && (*parse.term != ']')) { -	if (TCL_OK != Tcl_ParseCommand(NULL, p, bytesLeft, 1, &parse)) { -	    /* -	     * Syntax error.  Set the termOffset to the beginning of -	     * the last command parsed. -	     */ - -	    if (next == NULL) { -	        iPtr->termOffset = (parse.commandStart - 1) - script; -	    } else { -	        iPtr->termOffset = (next - 1) - script; -	    } -#ifndef TCL_TIP280 -	    return code; -#else -	    goto cleanup_return; -#endif -	} -	next = parse.commandStart + parse.commandSize; -	bytesLeft -= next - p; -	p = next; -	next = parse.commandStart; -	Tcl_FreeParse(&parse); -    } - -    if (bytesLeft) { -	/*  -	 * parse.term points to the close-bracket. -	 */ - -	iPtr->termOffset = parse.term - script; -    } else if (parse.term == script + numBytes) { -	/* -	 * There was no close-bracket.  Syntax error. -	 */ - -	iPtr->termOffset = parse.term - script; -	Tcl_SetObjResult(interp, -		Tcl_NewStringObj("missing close-bracket", -1)); -#ifndef TCL_TIP280 -	return TCL_ERROR; -#else -	code = TCL_ERROR; -	goto cleanup_return; -#endif -    } else if (*parse.term != ']') { -	/* -	 * There was no close-bracket.  Syntax error. -	 */ - -	iPtr->termOffset = (parse.term + 1) - script; -	Tcl_SetObjResult(interp, -		Tcl_NewStringObj("missing close-bracket", -1)); -#ifndef TCL_TIP280 -	return TCL_ERROR; -#else -	code = TCL_ERROR; -	goto cleanup_return; -#endif -    } else { -	/*  -	 * parse.term points to the close-bracket. -	 */ -	iPtr->termOffset = parse.term - script; +    iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; +    if (eeFramePtr->type == TCL_LOCATION_SOURCE) { +	Tcl_DecrRefCount(eeFramePtr->data.eval.path);      } +    TclStackFree(interp, linesStack); +    TclStackFree(interp, expandStack); +    TclStackFree(interp, stackObjArray); +    TclStackFree(interp, eeFramePtr); +    TclStackFree(interp, parsePtr); -#ifdef TCL_TIP280 - cleanup_return: -    /* TIP #280. Release the local CmdFrame, and its contents. */ - -    if (eeFrame.line != NULL) { -        ckfree ((char*) eeFrame.line); -    } -    if (eeFrame.type == TCL_LOCATION_SOURCE) { -        Tcl_DecrRefCount (eeFrame.data.eval.path); -    } -#endif      return code;  } -#ifdef TCL_TIP280  /*   *----------------------------------------------------------------------   *   * TclAdvanceLines --   * - *	This procedure is a helper which counts the number of lines - *	in a block of text and advances an external counter. + *	This function is a helper which counts the number of lines in a block + *	of text and advances an external counter.   *   * Results:   *	None. @@ -4302,21 +5398,71 @@ EvalEx(interp, script, numBytes, flags, line)   */  void -TclAdvanceLines (line,start,end) -     int*        line; -     CONST char* start; -     CONST char* end; +TclAdvanceLines( +    int *line, +    const char *start, +    const char *end)  { -    CONST char* p; +    register const char *p; +      for (p = start; p < end; p++) { -        if (*p == '\n') { -	  (*line) ++; +	if (*p == '\n') { +	    (*line)++;  	}      }  }  /*   *---------------------------------------------------------------------- + * + * TclAdvanceContinuations -- + * + *	This procedure is a helper which counts the number of continuation + *	lines (CL) in a block of text using a table of CL locations and + *	advances an external counter, and the pointer into the table. + * + * Results: + *	None. + * + * Side effects: + *	The specified counter is advanced per the number of continuation lines + *	found. + * + * TIP #280 + *---------------------------------------------------------------------- + */ + +void +TclAdvanceContinuations( +    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. +     * +     * *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)) { +	/* +	 * 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)++; +    } +} + +/* + *----------------------------------------------------------------------   * Note: The whole data structure access for argument location tracking is   * hidden behind these three functions. The only parts open are the lineLAPtr   * field in the Interp structure. The CFWord definition is internal to here. @@ -4329,8 +5475,8 @@ TclAdvanceLines (line,start,end)   *   * 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.   * @@ -4345,45 +5491,49 @@ TclAdvanceLines (line,start,end)   */  void -TclArgumentEnter(interp,objv,objc,cfPtr) -     Tcl_Interp* interp; -     Tcl_Obj**   objv; -     int         objc; -     CmdFrame*   cfPtr; +TclArgumentEnter( +    Tcl_Interp *interp, +    Tcl_Obj **objv, +    int objc, +    CmdFrame *cfPtr)  { -    Interp* iPtr = (Interp*) interp; +    Interp *iPtr = (Interp *) interp;      int new, i; -    Tcl_HashEntry* hPtr; -    CFWord* cfwPtr; +    Tcl_HashEntry *hPtr; +    CFWord *cfwPtr; -    for (i=1; i < objc; i++) { +    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' invokations, or +	 * 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, (char*) objv[i], &new); +	if (cfPtr->line[i] < 0) { +	    continue; +	} +	hPtr = Tcl_CreateHashEntry(iPtr->lineLAPtr, 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); +	    /* +	     * The word is not on the stack yet, remember the current location +	     * and initialize references. +	     */ + +	    cfwPtr = 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 = Tcl_GetHashValue(hPtr); +	    cfwPtr->refCount++;  	}      }  } @@ -4393,10 +5543,10 @@ TclArgumentEnter(interp,objv,objc,cfPtr)   *   * 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. @@ -4409,27 +5559,31 @@ TclArgumentEnter(interp,objv,objc,cfPtr)   */  void -TclArgumentRelease(interp,objv,objc) -     Tcl_Interp* interp; -     Tcl_Obj**   objv; -     int         objc; -{ -    Interp*        iPtr = (Interp*) interp; -    Tcl_HashEntry* hPtr; -    CFWord*        cfwPtr; +TclArgumentRelease( +    Tcl_Interp *interp, +    Tcl_Obj **objv, +    int objc) +{ +    Interp *iPtr = (Interp *) interp;      int i; -    for (i=1; i < objc; i++) { -       hPtr = Tcl_FindHashEntry (iPtr->lineLAPtr, (char *) objv[i]); +    for (i = 1; i < objc; i++) { +	CFWord *cfwPtr; +	Tcl_HashEntry *hPtr = +		Tcl_FindHashEntry(iPtr->lineLAPtr, (char *) objv[i]); -       if (!hPtr) { continue; } -       cfwPtr = (CFWord*) Tcl_GetHashValue (hPtr); +	if (!hPtr) { +	    continue; +	} +	cfwPtr = Tcl_GetHashValue(hPtr); -       cfwPtr->refCount --; -       if (cfwPtr->refCount > 0) { continue; } +	cfwPtr->refCount--; +	if (cfwPtr->refCount > 0) { +	    continue; +	} -       ckfree ((char*) cfwPtr); -       Tcl_DeleteHashEntry (hPtr); +	ckfree(cfwPtr); +	Tcl_DeleteHashEntry(hPtr);      }  } @@ -4438,9 +5592,9 @@ TclArgumentRelease(interp,objv,objc)   *   * 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 executed. 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: @@ -4454,47 +5608,94 @@ TclArgumentRelease(interp,objv,objc)   */  void -TclArgumentBCEnter(interp,codePtr,cfPtr) -     Tcl_Interp* interp; -     void*       codePtr; -     CmdFrame*   cfPtr; +TclArgumentBCEnter( +    Tcl_Interp *interp, +    Tcl_Obj *objv[], +    int objc, +    void *codePtr, +    CmdFrame *cfPtr, +    int cmd, +    int pc)  { -    Interp*        iPtr  = (Interp*) interp; -    Tcl_HashEntry* hePtr = Tcl_FindHashEntry (iPtr->lineBCPtr, (char *) codePtr); +    ExtCmdLoc *eclPtr; +    int word; +    ECL *ePtr; +    CFWordBC *lastPtr = NULL; +    Interp *iPtr = (Interp *) interp; +    Tcl_HashEntry *hePtr = +	    Tcl_FindHashEntry(iPtr->lineBCPtr, (char *) codePtr); -    if (hePtr) { -	ExtCmdLoc* eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hePtr); -	int i; +    if (!hePtr) { +	return; +    } +    eclPtr = Tcl_GetHashValue(hePtr); +    ePtr = &eclPtr->loc[cmd]; -	for (i=0; i < eclPtr->nueiloc; i++) { +    /* +     * 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; +    } -	    ExtIndex* eiPtr = &eclPtr->eiloc[i]; -	    Tcl_Obj*  obj   = eiPtr->obj; -	    int new; -	    Tcl_HashEntry*  hPtr; -	    CFWordBC* cfwPtr; +    /* +     * 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. +     */ -	    hPtr = Tcl_CreateHashEntry (iPtr->lineLABCPtr, (char*) obj, &new); -	    if (new) { +    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 = ckalloc(sizeof(CFWordBC)); + +	    cfwPtr->framePtr = cfPtr; +	    cfwPtr->obj = objv[word]; +	    cfwPtr->pc = pc; +	    cfwPtr->word = word; +	    cfwPtr->nextPtr = lastPtr; +	    lastPtr = cfwPtr; + +	    if (isnew) {  		/* -		 * The word is not on the stack yet, remember the current location -		 * and initialize references. +		 * The word is not on the stack yet, remember the current +		 * location and initialize references.  		 */ -		cfwPtr = (CFWordBC*) ckalloc (sizeof (CFWordBC)); -		cfwPtr->framePtr = cfPtr; -		cfwPtr->eiPtr    = eiPtr; -		cfwPtr->refCount = 1; -		Tcl_SetHashValue (hPtr, cfwPtr); + +		cfwPtr->prevPtr = NULL;  	    } else {  		/* -		 * The word is already on the stack, its current location is not -		 * relevant. Just remember the reference to prevent early removal. +		 * 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 = (CFWordBC*) Tcl_GetHashValue (hPtr); -		cfwPtr->refCount ++; + +		cfwPtr->prevPtr = Tcl_GetHashValue(hPtr);  	    } -	} /* for */ -    } /* if */ + +	    Tcl_SetHashValue(hPtr, cfwPtr); +	} +    } /* for */ + +    cfPtr->litarg = lastPtr;  }  /* @@ -4502,10 +5703,10 @@ TclArgumentBCEnter(interp,codePtr,cfPtr)   *   * 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. @@ -4518,33 +5719,34 @@ TclArgumentBCEnter(interp,codePtr,cfPtr)   */  void -TclArgumentBCRelease(interp,codePtr) -     Tcl_Interp* interp; -     void*       codePtr; +TclArgumentBCRelease( +    Tcl_Interp *interp, +    CmdFrame *cfPtr)  { -    Interp*        iPtr  = (Interp*) interp; -    Tcl_HashEntry* hePtr = Tcl_FindHashEntry (iPtr->lineBCPtr, (char *) codePtr); - -    if (hePtr) { -	ExtCmdLoc* eclPtr = (ExtCmdLoc*) Tcl_GetHashValue (hePtr); -	int i; +    Interp *iPtr = (Interp *) interp; +    CFWordBC *cfwPtr = (CFWordBC *) cfPtr->litarg; -	for (i=0; i < eclPtr->nueiloc; i++) { -	    Tcl_Obj*       obj  = eclPtr->eiloc[i].obj; -	    Tcl_HashEntry* hPtr = Tcl_FindHashEntry (iPtr->lineLABCPtr, (char *) obj); -	    CFWordBC* cfwPtr; +    while (cfwPtr) { +	CFWordBC *nextPtr = cfwPtr->nextPtr; +	Tcl_HashEntry *hPtr = +		Tcl_FindHashEntry(iPtr->lineLABCPtr, (char *) cfwPtr->obj); +	CFWordBC *xPtr = Tcl_GetHashValue(hPtr); -	    if (!hPtr) { continue; } +	if (xPtr != cfwPtr) { +	    Tcl_Panic("TclArgumentBC Enter/Release Mismatch"); +	} -	    cfwPtr = (CFWordBC*) Tcl_GetHashValue (hPtr); +	if (cfwPtr->prevPtr) { +	    Tcl_SetHashValue(hPtr, cfwPtr->prevPtr); +	} else { +	    Tcl_DeleteHashEntry(hPtr); +	} -	    cfwPtr->refCount --; -	    if (cfwPtr->refCount > 0) { continue; } +	ckfree(cfwPtr); +	cfwPtr = nextPtr; +    } -	    ckfree ((char*) cfwPtr); -	    Tcl_DeleteHashEntry (hPtr); -	} /* for */ -    } /* if */ +    cfPtr->litarg = NULL;  }  /* @@ -4552,8 +5754,8 @@ TclArgumentBCRelease(interp,codePtr)   *   * TclArgumentGet --   * - *	This procedure is a helper for the TIP #280 uplevel extension. - *	It find the location references for a Tcl_Obj, if any. + *	This procedure is a helper for the TIP #280 uplevel extension. It + *	finds the location references for a Tcl_Obj, if any.   *   * Results:   *	None. @@ -4566,36 +5768,37 @@ TclArgumentBCRelease(interp,codePtr)   */  void -TclArgumentGet(interp,obj,cfPtrPtr,wordPtr) -     Tcl_Interp* interp; -     Tcl_Obj*    obj; -     CmdFrame**  cfPtrPtr; -     int*        wordPtr; +TclArgumentGet( +    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 guaranteed to have been -     * generated dynamically: bail out, this cannot have a usable absolute -     * location. _Do not touch_ the information the set up by the caller. It -     * knows better than us. +     * An object which either has no string rep or else is a canonical list is +     * guaranteed to have been generated dynamically: bail out, this cannot +     * have a usable absolute location. _Do not touch_ the information the set +     * up by the caller. It knows better than us.       */ -    if (!obj->bytes) { +    if ((obj->bytes == NULL) || TclListObjIsCanonical(obj)) {  	return;      } -     +      /*       * First look for location information recorded in the argument       * stack. That is nearest.       */ -    hPtr = Tcl_FindHashEntry (iPtr->lineLAPtr, (char *) obj); +    hPtr = Tcl_FindHashEntry(iPtr->lineLAPtr, (char *) obj);      if (hPtr) { -	CFWord* cfwPtr = (CFWord*) Tcl_GetHashValue (hPtr); -	*wordPtr  = cfwPtr->word; +	CFWord *cfwPtr = Tcl_GetHashValue(hPtr); + +	*wordPtr = cfwPtr->word;  	*cfPtrPtr = cfwPtr->framePtr;  	return;      } @@ -4605,37 +5808,34 @@ TclArgumentGet(interp,obj,cfPtrPtr,wordPtr)       * that stack.       */ -    hPtr = Tcl_FindHashEntry (iPtr->lineLABCPtr, (char *) obj); +    hPtr = Tcl_FindHashEntry(iPtr->lineLABCPtr, (char *) obj);      if (hPtr) { -	CFWordBC* cfwPtr = (CFWordBC*) Tcl_GetHashValue (hPtr); -	ExtIndex* eiPtr = cfwPtr->eiPtr; +	CFWordBC *cfwPtr = Tcl_GetHashValue(hPtr);  	framePtr = cfwPtr->framePtr; -	framePtr->data.tebc.pc = ((ByteCode*) framePtr->data.tebc.codePtr)->codeStart + eiPtr->pc; +	framePtr->data.tebc.pc = (char *) (((ByteCode *) +		framePtr->data.tebc.codePtr)->codeStart + cfwPtr->pc);  	*cfPtrPtr = cfwPtr->framePtr; -	*wordPtr  = eiPtr->word; +	*wordPtr = cfwPtr->word;  	return;      }  } -#endif  /*   *----------------------------------------------------------------------   *   * Tcl_Eval --   * - *	Execute a Tcl command in a string.  This procedure executes the - *	script directly, rather than compiling it to bytecodes.  Before - *	the arrival of the bytecode compiler in Tcl 8.0 Tcl_Eval was - *	the main procedure used for executing Tcl commands, but nowadays - *	it isn't used much. + *	Execute a Tcl command in a string. This function executes the script + *	directly, rather than compiling it to bytecodes. Before the arrival of + *	the bytecode compiler in Tcl 8.0 Tcl_Eval was the main function used + *	for executing Tcl commands, but nowadays it isn't used much.   *   * Results: - *	The return value is one of the return codes defined in tcl.h - *	(such as TCL_OK), and interp's result contains a value - *	to supplement the return code. The value of the result - *	will persist only until the next call to Tcl_Eval or Tcl_EvalObj: - *	you must copy it or lose it! + *	The return value is one of the return codes defined in tcl.h (such as + *	TCL_OK), and interp's result contains a value to supplement the return + *	code. The value of the result will persist only until the next call to + *	Tcl_Eval or Tcl_EvalObj: you must copy it or lose it!   *   * Side effects:   *	Can be almost arbitrary, depending on the commands in the script. @@ -4643,22 +5843,22 @@ TclArgumentGet(interp,obj,cfPtrPtr,wordPtr)   *----------------------------------------------------------------------   */ +#undef Tcl_Eval  int -Tcl_Eval(interp, string) -    Tcl_Interp *interp;		/* Token for command interpreter (returned -				 * by previous call to Tcl_CreateInterp). */ -    CONST char *string;		/* Pointer to TCL command to execute. */ +Tcl_Eval( +    Tcl_Interp *interp,		/* Token for command interpreter (returned by +				 * previous call to Tcl_CreateInterp). */ +    const char *script)		/* Pointer to TCL command to execute. */  { -    int code = Tcl_EvalEx(interp, string, -1, 0); +    int code = Tcl_EvalEx(interp, script, -1, 0);      /* -     * For backwards compatibility with old C code that predates the -     * object system in Tcl 8.0, we have to mirror the object result -     * back into the string result (some callers may expect it there). +     * For backwards compatibility with old C code that predates the object +     * system in Tcl 8.0, we have to mirror the object result back into the +     * string result (some callers may expect it there).       */ -    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	    TCL_VOLATILE); +    (void) Tcl_GetStringResult(interp);      return code;  } @@ -4681,18 +5881,17 @@ Tcl_Eval(interp, string)  #undef Tcl_EvalObj  int -Tcl_EvalObj(interp, objPtr) -    Tcl_Interp * interp; -    Tcl_Obj * objPtr; +Tcl_EvalObj( +    Tcl_Interp *interp, +    Tcl_Obj *objPtr)  {      return Tcl_EvalObjEx(interp, objPtr, 0);  } -  #undef Tcl_GlobalEvalObj  int -Tcl_GlobalEvalObj(interp, objPtr) -    Tcl_Interp * interp; -    Tcl_Obj * objPtr; +Tcl_GlobalEvalObj( +    Tcl_Interp *interp, +    Tcl_Obj *objPtr)  {      return Tcl_EvalObjEx(interp, objPtr, TCL_EVAL_GLOBAL);  } @@ -4703,329 +5902,342 @@ Tcl_GlobalEvalObj(interp, objPtr)   * Tcl_EvalObjEx, TclEvalObjEx --   *   *	Execute Tcl commands stored in a Tcl object. These commands are - *	compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT - *	is specified. + *	compiled into bytecodes if necessary, unless TCL_EVAL_DIRECT is + *	specified. + * + *	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 the return code. + *	The return value is one of the return codes defined in tcl.h (such as + *	TCL_OK), and the interpreter's result contains a value to supplement + *	the return code.   *   * Side effects: - *	The object is converted, if necessary, to a ByteCode object that - *	holds the bytecode instructions for the commands. Executing the - *	commands will almost certainly have side effects that depend - *	on those commands. - * - *	Just as in Tcl_Eval, interp->termOffset is set to the offset of the - *	last character executed in the objPtr's string. + *	The object is converted, if necessary, to a ByteCode object that holds + *	the bytecode instructions for the commands. Executing the commands + *	will almost certainly have side effects that depend on those commands.   *   * TIP #280 : Keep public API, internally extended API.   *----------------------------------------------------------------------   */  int -Tcl_EvalObjEx(interp, objPtr, flags) -    Tcl_Interp *interp;			/* Token for command interpreter -					 * (returned by a previous call to -					 * Tcl_CreateInterp). */ -    register Tcl_Obj *objPtr;		/* Pointer to object containing -					 * commands to execute. */ -    int flags;				/* Collection of OR-ed bits that -					 * control the evaluation of the -					 * script.  Supported values are -					 * TCL_EVAL_GLOBAL and -					 * TCL_EVAL_DIRECT. */ -{ -#ifdef TCL_TIP280 -  return TclEvalObjEx (interp, objPtr, flags, NULL, 0); +Tcl_EvalObjEx( +    Tcl_Interp *interp,		/* Token for command interpreter (returned by +				 * a previous call to Tcl_CreateInterp). */ +    register Tcl_Obj *objPtr,	/* Pointer to object containing commands to +				 * execute. */ +    int flags)			/* Collection of OR-ed bits that control the +				 * evaluation of the script. Supported values +				 * are TCL_EVAL_GLOBAL and TCL_EVAL_DIRECT. */ +{ +    return TclEvalObjEx(interp, objPtr, flags, NULL, 0);  }  int -TclEvalObjEx(interp, objPtr, flags, invoker, word) -    Tcl_Interp *interp;			/* Token for command interpreter -					 * (returned by a previous call to -					 * Tcl_CreateInterp). */ -    register Tcl_Obj *objPtr;		/* Pointer to object containing -					 * commands to execute. */ -    int flags;				/* Collection of OR-ed bits that -					 * control the evaluation of the -					 * script.  Supported values are -					 * TCL_EVAL_GLOBAL and -					 * TCL_EVAL_DIRECT. */ -    CONST CmdFrame* invoker; /* Frame of the command doing the eval  */ -    int             word;    /* Index of the word which is in objPtr */ +TclEvalObjEx( +    Tcl_Interp *interp,		/* Token for command interpreter (returned by +				 * a previous call to Tcl_CreateInterp). */ +    register Tcl_Obj *objPtr,	/* Pointer to object containing commands to +				 * execute. */ +    int flags,			/* Collection of OR-ed bits that control the +				 * evaluation of the script. Supported values +				 * are TCL_EVAL_GLOBAL and TCL_EVAL_DIRECT. */ +    const CmdFrame *invoker,	/* Frame of the command doing the eval. */ +    int word)			/* Index of the word which is in objPtr. */  { -#endif -    register Interp *iPtr = (Interp *) interp; -    char *script; -    int numSrcBytes; +    int result = 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). */ +    register Tcl_Obj *objPtr,	/* Pointer to object containing commands to +				 * execute. */ +    int flags,			/* Collection of OR-ed bits that control the +				 * evaluation of the script. Supported values +				 * are TCL_EVAL_GLOBAL and TCL_EVAL_DIRECT. */ +    const CmdFrame *invoker,	/* Frame of the command doing the eval. */ +    int word)			/* Index of the word which is in objPtr. */ +{ +    Interp *iPtr = (Interp *) interp;      int result; -    CallFrame *savedVarFramePtr;	/* Saves old copy of iPtr->varFramePtr -					 * in case TCL_EVAL_GLOBAL was set. */ -    int allowExceptions = (iPtr->evalFlags & TCL_ALLOW_EXCEPTIONS); -    Tcl_IncrRefCount(objPtr); +    /* +     * 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. +     */ + +    if (TclListObjIsCanonical(objPtr)) { +	CmdFrame *eoFramePtr = NULL; +	int objc; +	Tcl_Obj *listPtr, **objv; -    if ((iPtr->flags & USE_EVAL_DIRECT) || (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). -	 * -	 * 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. +	 * 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.  	 * -	 * USE_EVAL_DIRECT is a special flag used for testing purpose only -	 * (ensure we go into the TCL_EVAL_DIRECT path, avoiding opt) +	 * This also preserves any associations between list elements and +	 * location information for such elements.  	 */ -	if (!(iPtr->flags & USE_EVAL_DIRECT) && -		(objPtr->typePtr == &tclListType) && /* is a list... */ -		(objPtr->bytes == NULL) /* ...without a string rep */) { -	    register List *listRepPtr = -		(List *) objPtr->internalRep.twoPtrValue.ptr1; -	    int i, objc = listRepPtr->elemCount; - -#define TEOE_PREALLOC 10 -	    Tcl_Obj *staticObjv[TEOE_PREALLOC], **objv = staticObjv; - -#ifdef TCL_TIP280 -	    /* TIP #280 Structures for tracking lines. -	     * As we know that this is dynamic execution we ignore the -	     * invoker, even if known. -	     */ -	    CmdFrame eoFrame; - -	    eoFrame.type     = TCL_LOCATION_EVAL_LIST; -	    eoFrame.level    = (iPtr->cmdFramePtr == NULL ? -				1 : -				iPtr->cmdFramePtr->level + 1); -	    eoFrame.framePtr = iPtr->framePtr; -	    eoFrame.nextPtr  = iPtr->cmdFramePtr; -	    eoFrame.nline    = 0; -	    eoFrame.line     = NULL; - -	    /* NOTE: Getting the string rep of the list to eval to fill the -	     * command information required by 'info frame' implies that -	     * further calls for the same list would not be optimized, as it -	     * would not be 'pure' anymore. It would also be a waste of time -	     * as most of the time this information is not needed at all. What -	     * we do instead is to keep the list obj itself around and have -	     * 'info frame' sort it out. -	     */ -	    eoFrame.cmd.listPtr  = objPtr; -	    Tcl_IncrRefCount (eoFrame.cmd.listPtr); -	    eoFrame.data.eval.path = NULL; -#endif -	    if (objc > TEOE_PREALLOC) { -		objv = (Tcl_Obj **) ckalloc(objc*sizeof(Tcl_Obj *)); -	    } -#undef TEOE_PREALLOC -	    /* -	     * Copy the list elements here, to avoid a segfault if -	     * objPtr loses its List internal rep [Bug 1119369]. -	     * -	     * TIP #280 We do _not_ compute all the line numbers for the words -	     * in the command. For the eval of a pure list the most sensible -	     * choice is to put all words on line 1. Given that we neither -	     * need memory for them nor compute anything. 'line' is left -	     * NULL. The two places using this information (TclInfoFrame, and -	     * TclInitCompileEnv), are special-cased to use the proper line -	     * number directly instead of accessing the 'line' array. -	     */ - -	    for (i=0; i < objc; i++) { -		objv[i] = listRepPtr->elements[i]; -		Tcl_IncrRefCount(objv[i]); -	    } +	/* +	 * 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 od 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? +	 */ -#ifdef TCL_TIP280 -	    iPtr->cmdFramePtr = &eoFrame; -#endif -	    result = Tcl_EvalObjv(interp, objc, objv, flags); -#ifdef TCL_TIP280 -	    iPtr->cmdFramePtr = iPtr->cmdFramePtr->nextPtr; -	    Tcl_DecrRefCount (eoFrame.cmd.listPtr); -#endif +	Tcl_IncrRefCount(objPtr); +	listPtr = TclListObjCopy(interp, objPtr); +	Tcl_IncrRefCount(listPtr); -	    for (i=0; i < objc; i++) { -		TclDecrRefCount(objv[i]); -	    } -	    if (objv != staticObjv) { -		ckfree((char *) objv); -	    } -#ifdef TCL_TIP280 -	    ckfree ((char*) eoFrame.line); -	    eoFrame.line  = NULL; -	    eoFrame.nline = 0; -#endif -	} else { -#ifndef TCL_TIP280 -	    script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); -	    result = Tcl_EvalEx(interp, script, numSrcBytes, flags); -#else +	if (word != INT_MIN) {  	    /* -	     * 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. +	     * 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.  	     * -	     * See also tclCompile.c, TclInitCompileEnv, for the equivalent -	     * code in the bytecode compiler. +	     * Note that we use (word==INTMIN) to signal that no command frame +	     * should be pushed, as needed by alias and ensemble redirections.  	     */ -	    if (invoker == NULL) { -	        /* No context, force opening of our own */ -	        script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); -		result = Tcl_EvalEx(interp, script, numSrcBytes, flags); -	    } else { -		/* We have an invoker, describing the command asking for the -		 * evaluation of a subordinate script. This script may -		 * originate in a literal word, or from a variable, etc. Using -		 * the line array we now check if we have good line -		 * information for the relevant word. The type of context is -		 * relevant as well. In a non-'source' context we don't have -		 * to try tracking lines. -		 * -		 * First see if the word exists and is a literal. If not we go -		 * through the easy dynamic branch. No need to perform more -		 * complex invokations. -		 */ - -		CmdFrame ctx = *invoker; -		int pc       = 0; +	    eoFramePtr = TclStackAlloc(interp, sizeof(CmdFrame)); +	    eoFramePtr->nline = 0; +	    eoFramePtr->line = NULL; -		if (invoker->type == TCL_LOCATION_BC) { -		    /* Note: Type BC => ctx.data.eval.path    is not used. -		     *                  ctx.data.tebc.codePtr is used instead. -		     */ -		    TclGetSrcInfoForPc (&ctx); -		    pc = 1; -		} +	    eoFramePtr->type = TCL_LOCATION_EVAL; +	    eoFramePtr->level = (iPtr->cmdFramePtr == NULL? +		    1 : iPtr->cmdFramePtr->level + 1); +	    eoFramePtr->framePtr = iPtr->framePtr; +	    eoFramePtr->nextPtr = iPtr->cmdFramePtr; -                script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); +	    eoFramePtr->cmdObj = objPtr; +	    eoFramePtr->cmd = NULL; +	    eoFramePtr->len = 0; +	    eoFramePtr->data.eval.path = NULL; -		if ((ctx.nline <= word) || -		    (ctx.line[word] < 0) || -		    (ctx.type != TCL_LOCATION_SOURCE)) { -		    /* Dynamic script, or dynamic context, force our own -		     * context */ +	    iPtr->cmdFramePtr = eoFramePtr; -		    result = Tcl_EvalEx(interp, script, numSrcBytes, flags); -		} else { -		    /* Absolute context available to reuse. */ +	    flags |= TCL_EVAL_SOURCE_IN_FRAME; +	} -		    iPtr->invokeCmdFramePtr = &ctx; -		    iPtr->evalFlags |= TCL_EVAL_CTX; +	TclMarkTailcall(interp); +        TclNRAddCallback(interp, TEOEx_ListCallback, listPtr, eoFramePtr, +		objPtr, NULL); -		    result = EvalEx(interp, script, numSrcBytes, flags, ctx.line [word]); +	ListObjGetElements(listPtr, objc, objv); +	return TclNREvalObjv(interp, objc, objv, flags, NULL); +    } -		    if (pc) { -			/* Death of SrcInfo reference */ -			Tcl_DecrRefCount (ctx.data.eval.path); -		    } -		} -	    } -#endif -	} -    } else { +    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 The invoker provides us with the context for the script. +	 * We transfer this to the byte code compiler.  	 */ -	savedVarFramePtr = iPtr->varFramePtr; +	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) { -	    iPtr->varFramePtr = NULL; +	    savedVarFramePtr = iPtr->varFramePtr; +	    iPtr->varFramePtr = iPtr->rootFramePtr;  	} +	Tcl_IncrRefCount(objPtr); +	codePtr = TclCompileObj(interp, objPtr, invoker, word); -#ifndef TCL_TIP280 -	result = TclCompEvalObj(interp, objPtr); -#else -	result = TclCompEvalObj(interp, objPtr, invoker, word); -#endif +	TclNRAddCallback(interp, TEOEx_ByteCodeCallback, savedVarFramePtr, +		objPtr, INT2PTR(allowExceptions), NULL); +        return TclNRExecuteByteCode(interp, codePtr); +    } +    {  	/* -	 * If we are again at the top level, process any unusual  -	 * return code returned by the evaluated code.  +	 * We're not supposed to use the compiler or byte-code +	 * interpreter. Let Tcl_EvalEx evaluate the command directly (and +	 * probably more slowly).  	 */ -	 -	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; -		/* -		 * If an error was created here, record information about  -		 * what was being executed when the error occurred. Remove -		 * the extra \n added by tclMain.c in the command sent to -		 * Tcl_LogCommandInfo [Bug 833150]. -		 */ +	const char *script; +	int numSrcBytes; -		if (!(iPtr->flags & ERR_ALREADY_LOGGED)) { -		    script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); -		    Tcl_LogCommandInfo(interp, script, script, --numSrcBytes); -		    iPtr->flags &= ~ERR_ALREADY_LOGGED; -		} -	    } +	/* +	 * Now we check if we have data about invisible continuation lines for +	 * the script, and make it available to the direct script parser and +	 * evaluator we are about to call, if so. +	 * +	 * It may be possible that the script Tcl_Obj* can be free'd while the +	 * evaluator is using it, leading to the release of the associated +	 * ContLineLoc structure as well. To ensure that the latter doesn't +	 * happen we set a lock on it. We release this lock later in this +	 * function, after the evaluator is done. The relevant "lineCLPtr" +	 * hashtable is managed in the file "tclObj.c". +	 * +	 * Another important action is to save (and later restore) the +	 * continuation line information of the caller, in case we are +	 * executing nested commands in the eval/direct path. +	 */ + +	ContLineLoc *saveCLLocPtr = iPtr->scriptCLLocPtr; + +	assert(invoker == NULL); + +	iPtr->scriptCLLocPtr = TclContinuationsGet(objPtr); + +	Tcl_IncrRefCount(objPtr); + +	script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); +	result = Tcl_EvalEx(interp, script, numSrcBytes, flags); + +	TclDecrRefCount(objPtr); + +	iPtr->scriptCLLocPtr = saveCLLocPtr; +	return result; +    } +} + +static int +TEOEx_ByteCodeCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    CallFrame *savedVarFramePtr = data[0]; +    Tcl_Obj *objPtr = data[1]; +    int allowExceptions = PTR2INT(data[2]); + +    if (iPtr->numLevels == 0) { +	if (result == TCL_RETURN) { +	    result = TclUpdateReturnInfo(iPtr); +	} +	if ((result != TCL_OK) && (result != TCL_ERROR) && !allowExceptions) { +	    const char *script; +	    int numSrcBytes; + +	    ProcessUnexpectedResult(interp, result); +	    result = TCL_ERROR; +	    script = Tcl_GetStringFromObj(objPtr, &numSrcBytes); +	    Tcl_LogCommandInfo(interp, script, script, numSrcBytes);  	} -	iPtr->evalFlags = 0; -	iPtr->varFramePtr = savedVarFramePtr;  + +	/* +	 * We are returning to level 0, so should call TclResetCancellation. +	 * Let us just unset the flags inline. +	 */ + +	TclUnsetCancelFlags(iPtr); +    } +    iPtr->evalFlags = 0; + +    /* +     * Restore the callFrame if this was a TCL_EVAL_GLOBAL. +     */ + +    if (savedVarFramePtr) { +	iPtr->varFramePtr = savedVarFramePtr;      }      TclDecrRefCount(objPtr);      return result;  } + +static int +TEOEx_ListCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    Tcl_Obj *listPtr = data[0]; +    CmdFrame *eoFramePtr = data[1]; +    Tcl_Obj *objPtr = data[2]; + +    /* +     * Remove the cmdFrame +     */ + +    if (eoFramePtr) { +	iPtr->cmdFramePtr = eoFramePtr->nextPtr; +	TclStackFree(interp, eoFramePtr); +    } +    TclDecrRefCount(objPtr); +    TclDecrRefCount(listPtr); + +    return result; +}  /*   *----------------------------------------------------------------------   *   * ProcessUnexpectedResult --   * - *	Procedure called by Tcl_EvalObj to set the interpreter's result - *	value to an appropriate error message when the code it evaluates - *	returns an unexpected result code (not TCL_OK and not TCL_ERROR) to - *	the topmost evaluation level. + *	Function called by Tcl_EvalObj to set the interpreter's result value + *	to an appropriate error message when the code it evaluates returns an + *	unexpected result code (not TCL_OK and not TCL_ERROR) to the topmost + *	evaluation level.   *   * Results:   *	None.   *   * Side effects: - *	The interpreter result is set to an error message appropriate to - *	the result code. + *	The interpreter result is set to an error message appropriate to the + *	result code.   *   *----------------------------------------------------------------------   */  static void -ProcessUnexpectedResult(interp, returnCode) -    Tcl_Interp *interp;		/* The interpreter in which the unexpected +ProcessUnexpectedResult( +    Tcl_Interp *interp,		/* The interpreter in which the unexpected  				 * result code was returned. */ -    int returnCode;		/* The unexpected result code. */ +    int returnCode)		/* The unexpected result code. */  { +    char buf[TCL_INTEGER_SPACE]; +      Tcl_ResetResult(interp);      if (returnCode == TCL_BREAK) { -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "invoked \"break\" outside of a loop", -1); +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"invoked \"break\" outside of a loop", -1));      } else if (returnCode == TCL_CONTINUE) { -	Tcl_AppendToObj(Tcl_GetObjResult(interp), -		"invoked \"continue\" outside of a loop", -1); +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"invoked \"continue\" outside of a loop", -1));      } else { -        char buf[30 + TCL_INTEGER_SPACE]; - -	sprintf(buf, "command returned bad code: %d", returnCode); -	Tcl_SetResult(interp, buf, TCL_VOLATILE); +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +		"command returned bad code: %d", returnCode));      } +    sprintf(buf, "%d", returnCode); +    Tcl_SetErrorCode(interp, "TCL", "UNEXPECTED_RESULT_CODE", buf, NULL);  }  /* @@ -5033,15 +6245,15 @@ ProcessUnexpectedResult(interp, returnCode)   *   * Tcl_ExprLong, Tcl_ExprDouble, Tcl_ExprBoolean --   * - *	Procedures to evaluate an expression and return its value in a + *	Functions to evaluate an expression and return its value in a   *	particular form.   *   * Results: - *	Each of the procedures below returns a standard Tcl result. If an - *	error occurs then an error message is left in the interp's result. - *	Otherwise the value of the expression, in the appropriate form, - *	is stored at *ptr. If the expression had a result that was - *	incompatible with the desired form then an error is returned. + *	Each of the functions below returns a standard Tcl result. If an error + *	occurs then an error message is left in the interp's result. Otherwise + *	the value of the expression, in the appropriate form, is stored at + *	*ptr. If the expression had a result that was incompatible with the + *	desired form then an error is returned.   *   * Side effects:   *	None. @@ -5050,197 +6262,92 @@ ProcessUnexpectedResult(interp, returnCode)   */  int -Tcl_ExprLong(interp, string, ptr) -    Tcl_Interp *interp;		/* Context in which to evaluate the +Tcl_ExprLong( +    Tcl_Interp *interp,		/* Context in which to evaluate the  				 * expression. */ -    CONST char *string;		/* Expression to evaluate. */ -    long *ptr;			/* Where to store result. */ +    const char *exprstring,	/* Expression to evaluate. */ +    long *ptr)			/* Where to store result. */  {      register Tcl_Obj *exprPtr; -    Tcl_Obj *resultPtr; -    int length = strlen(string);      int result = TCL_OK; - -    if (length > 0) { -	exprPtr = Tcl_NewStringObj(string, length); -	Tcl_IncrRefCount(exprPtr); -	result = Tcl_ExprObj(interp, exprPtr, &resultPtr); -	if (result == TCL_OK) { -	    /* -	     * Store an integer based on the expression result. -	     */ - -	    if (resultPtr->typePtr == &tclIntType) { -		*ptr = resultPtr->internalRep.longValue; -	    } else if (resultPtr->typePtr == &tclDoubleType) { -		*ptr = (long) resultPtr->internalRep.doubleValue; -	    } else if (resultPtr->typePtr == &tclWideIntType) { -#ifndef TCL_WIDE_INT_IS_LONG -		/* -		 * See Tcl_GetIntFromObj for conversion comments. -		 */ -		Tcl_WideInt w = resultPtr->internalRep.wideValue; -		if ((w >= -(Tcl_WideInt)(ULONG_MAX)) -			&& (w <= (Tcl_WideInt)(ULONG_MAX))) { -		    *ptr = Tcl_WideAsLong(w); -		} else { -		    Tcl_SetResult(interp, -			    "integer value too large to represent as non-long integer", -			    TCL_STATIC); -		    result = TCL_ERROR; -		} -#else -		*ptr = resultPtr->internalRep.longValue; -#endif -	    } else { -		Tcl_SetResult(interp, -		        "expression didn't have numeric value", TCL_STATIC); -		result = TCL_ERROR; -	    } -	    Tcl_DecrRefCount(resultPtr);  /* discard the result object */ -	} else { -	    /* -	     * Move the interpreter's object result to the string result,  -	     * then reset the object result. -	     */ - -	    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	            TCL_VOLATILE); -	} -	Tcl_DecrRefCount(exprPtr);  /* discard the expression object */	 -    } else { +    if (*exprstring == '\0') {  	/* -	 * An empty string. Just set the result integer to 0. +	 * Legacy compatibility - return 0 for the zero-length string.  	 */ -	 +  	*ptr = 0; +    } else { +	exprPtr = Tcl_NewStringObj(exprstring, -1); +	Tcl_IncrRefCount(exprPtr); +	result = Tcl_ExprLongObj(interp, exprPtr, ptr); +	Tcl_DecrRefCount(exprPtr); +	if (result != TCL_OK) { +	    (void) Tcl_GetStringResult(interp); +	}      }      return result;  }  int -Tcl_ExprDouble(interp, string, ptr) -    Tcl_Interp *interp;		/* Context in which to evaluate the +Tcl_ExprDouble( +    Tcl_Interp *interp,		/* Context in which to evaluate the  				 * expression. */ -    CONST char *string;		/* Expression to evaluate. */ -    double *ptr;		/* Where to store result. */ +    const char *exprstring,	/* Expression to evaluate. */ +    double *ptr)		/* Where to store result. */  {      register Tcl_Obj *exprPtr; -    Tcl_Obj *resultPtr; -    int length = strlen(string);      int result = TCL_OK; -    if (length > 0) { -	exprPtr = Tcl_NewStringObj(string, length); -	Tcl_IncrRefCount(exprPtr); -	result = Tcl_ExprObj(interp, exprPtr, &resultPtr); -	if (result == TCL_OK) { -	    /* -	     * Store a double  based on the expression result. -	     */ - -	    if (resultPtr->typePtr == &tclIntType) { -		*ptr = (double) resultPtr->internalRep.longValue; -	    } else if (resultPtr->typePtr == &tclDoubleType) { -		*ptr = resultPtr->internalRep.doubleValue; -	    } else if (resultPtr->typePtr == &tclWideIntType) { -#ifndef TCL_WIDE_INT_IS_LONG -		/* -		 * See Tcl_GetIntFromObj for conversion comments. -		 */ -		Tcl_WideInt w = resultPtr->internalRep.wideValue; -		if ((w >= -(Tcl_WideInt)(ULONG_MAX)) -			&& (w <= (Tcl_WideInt)(ULONG_MAX))) { -		    *ptr = (double) Tcl_WideAsLong(w); -		} else { -		    Tcl_SetResult(interp, -			    "integer value too large to represent as non-long integer", -			    TCL_STATIC); -		    result = TCL_ERROR; -		} -#else -		*ptr = (double) resultPtr->internalRep.longValue; -#endif -	    } else { -		Tcl_SetResult(interp, -		        "expression didn't have numeric value", TCL_STATIC); -		result = TCL_ERROR; -	    } -	    Tcl_DecrRefCount(resultPtr);  /* discard the result object */ -	} else { -	    /* -	     * Move the interpreter's object result to the string result,  -	     * then reset the object result. -	     */ - -	    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	            TCL_VOLATILE); -	} -	Tcl_DecrRefCount(exprPtr);  /* discard the expression object */ -    } else { +    if (*exprstring == '\0') {  	/* -	 * An empty string. Just set the result double to 0.0. +	 * Legacy compatibility - return 0 for the zero-length string.  	 */ -	 +  	*ptr = 0.0; +    } else { +	exprPtr = Tcl_NewStringObj(exprstring, -1); +	Tcl_IncrRefCount(exprPtr); +	result = Tcl_ExprDoubleObj(interp, exprPtr, ptr); +	Tcl_DecrRefCount(exprPtr); +				/* Discard the expression object. */ +	if (result != TCL_OK) { +	    (void) Tcl_GetStringResult(interp); +	}      }      return result;  }  int -Tcl_ExprBoolean(interp, string, ptr) -    Tcl_Interp *interp;		/* Context in which to evaluate the -			         * expression. */ -    CONST char *string;		/* Expression to evaluate. */ -    int *ptr;			/* Where to store 0/1 result. */ +Tcl_ExprBoolean( +    Tcl_Interp *interp,		/* Context in which to evaluate the +				 * expression. */ +    const char *exprstring,	/* Expression to evaluate. */ +    int *ptr)			/* Where to store 0/1 result. */  { -    register Tcl_Obj *exprPtr; -    Tcl_Obj *resultPtr; -    int length = strlen(string); -    int result = TCL_OK; +    if (*exprstring == '\0') { +	/* +	 * An empty string. Just set the result boolean to 0 (false). +	 */ -    if (length > 0) { -	exprPtr = Tcl_NewStringObj(string, length); -	Tcl_IncrRefCount(exprPtr); -	result = Tcl_ExprObj(interp, exprPtr, &resultPtr); -	if (result == TCL_OK) { -	    /* -	     * Store a boolean based on the expression result. -	     */ +	*ptr = 0; +	return TCL_OK; +    } else { +	int result; +	Tcl_Obj *exprPtr = Tcl_NewStringObj(exprstring, -1); -	    if (resultPtr->typePtr == &tclIntType) { -		*ptr = (resultPtr->internalRep.longValue != 0); -	    } else if (resultPtr->typePtr == &tclDoubleType) { -		*ptr = (resultPtr->internalRep.doubleValue != 0.0); -	    } else if (resultPtr->typePtr == &tclWideIntType) { -#ifndef TCL_WIDE_INT_IS_LONG -		*ptr = (resultPtr->internalRep.wideValue != 0); -#else -		*ptr = (resultPtr->internalRep.longValue != 0); -#endif -	    } else { -		result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); -	    } -	    Tcl_DecrRefCount(resultPtr);  /* discard the result object */ -	} +	Tcl_IncrRefCount(exprPtr); +	result = Tcl_ExprBooleanObj(interp, exprPtr, ptr); +	Tcl_DecrRefCount(exprPtr);  	if (result != TCL_OK) {  	    /* -	     * Move the interpreter's object result to the string result,  -	     * then reset the object result. +	     * Move the interpreter's object result to the string result, then +	     * reset the object result.  	     */ -	    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	            TCL_VOLATILE); +	    (void) Tcl_GetStringResult(interp);  	} -	Tcl_DecrRefCount(exprPtr); /* discard the expression object */ -    } else { -	/* -	 * An empty string. Just set the result boolean to 0 (false). -	 */ -	 -	*ptr = 0; +	return result;      } -    return result;  }  /* @@ -5248,16 +6355,15 @@ Tcl_ExprBoolean(interp, string, ptr)   *   * Tcl_ExprLongObj, Tcl_ExprDoubleObj, Tcl_ExprBooleanObj --   * - *	Procedures to evaluate an expression in an object and return its - *	value in a particular form. + *	Functions to evaluate an expression in an object and return its value + *	in a particular form.   *   * Results: - *	Each of the procedures below returns a standard Tcl result - *	object. If an error occurs then an error message is left in the - *	interpreter's result. Otherwise the value of the expression, in the - *	appropriate form, is stored at *ptr. If the expression had a result - *	that was incompatible with the desired form then an error is - *	returned. + *	Each of the functions below returns a standard Tcl result object. If + *	an error occurs then an error message is left in the interpreter's + *	result. Otherwise the value of the expression, in the appropriate + *	form, is stored at *ptr. If the expression had a result that was + *	incompatible with the desired form then an error is returned.   *   * Side effects:   *	None. @@ -5266,79 +6372,104 @@ Tcl_ExprBoolean(interp, string, ptr)   */  int -Tcl_ExprLongObj(interp, objPtr, ptr) -    Tcl_Interp *interp;			/* Context in which to evaluate the -					 * expression. */ -    register Tcl_Obj *objPtr;		/* Expression to evaluate. */ -    long *ptr;				/* Where to store long result. */ +Tcl_ExprLongObj( +    Tcl_Interp *interp,		/* Context in which to evaluate the +				 * expression. */ +    register Tcl_Obj *objPtr,	/* Expression to evaluate. */ +    long *ptr)			/* Where to store long result. */  {      Tcl_Obj *resultPtr; -    int result; +    int result, type; +    double d; +    ClientData internalPtr;      result = Tcl_ExprObj(interp, objPtr, &resultPtr); -    if (result == TCL_OK) { -	if (resultPtr->typePtr == &tclIntType) { -	    *ptr = resultPtr->internalRep.longValue; -	} else if (resultPtr->typePtr == &tclDoubleType) { -	    *ptr = (long) resultPtr->internalRep.doubleValue; -	} else { -	    result = Tcl_GetLongFromObj(interp, resultPtr, ptr); -	    if (result != TCL_OK) { -		return result; -	    } +    if (result != TCL_OK) { +	return TCL_ERROR; +    } + +    if (TclGetNumberFromObj(interp, resultPtr, &internalPtr, &type)!=TCL_OK) { +	return TCL_ERROR; +    } + +    switch (type) { +    case TCL_NUMBER_DOUBLE: { +	mp_int big; + +	d = *((const double *) internalPtr); +	Tcl_DecrRefCount(resultPtr); +	if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { +	    return TCL_ERROR;  	} -	Tcl_DecrRefCount(resultPtr);  /* discard the result object */ +	resultPtr = Tcl_NewBignumObj(&big); +	/* FALLTHROUGH */      } +    case TCL_NUMBER_LONG: +    case TCL_NUMBER_WIDE: +    case TCL_NUMBER_BIG: +	result = TclGetLongFromObj(interp, resultPtr, ptr); +	break; + +    case TCL_NUMBER_NAN: +	Tcl_GetDoubleFromObj(interp, resultPtr, &d); +	result = TCL_ERROR; +    } + +    Tcl_DecrRefCount(resultPtr);/* Discard the result object. */      return result;  }  int -Tcl_ExprDoubleObj(interp, objPtr, ptr) -    Tcl_Interp *interp;			/* Context in which to evaluate the -					 * expression. */ -    register Tcl_Obj *objPtr;		/* Expression to evaluate. */ -    double *ptr;			/* Where to store double result. */ +Tcl_ExprDoubleObj( +    Tcl_Interp *interp,		/* Context in which to evaluate the +				 * expression. */ +    register Tcl_Obj *objPtr,	/* Expression to evaluate. */ +    double *ptr)		/* Where to store double result. */  {      Tcl_Obj *resultPtr; -    int result; +    int result, type; +    ClientData internalPtr;      result = Tcl_ExprObj(interp, objPtr, &resultPtr); +    if (result != TCL_OK) { +	return TCL_ERROR; +    } + +    result = TclGetNumberFromObj(interp, resultPtr, &internalPtr, &type);      if (result == TCL_OK) { -	if (resultPtr->typePtr == &tclIntType) { -	    *ptr = (double) resultPtr->internalRep.longValue; -	} else if (resultPtr->typePtr == &tclDoubleType) { -	    *ptr = resultPtr->internalRep.doubleValue; -	} else { +	switch (type) { +	case TCL_NUMBER_NAN: +#ifndef ACCEPT_NAN +	    result = Tcl_GetDoubleFromObj(interp, resultPtr, ptr); +	    break; +#endif +	case TCL_NUMBER_DOUBLE: +	    *ptr = *((const double *) internalPtr); +	    result = TCL_OK; +	    break; +	default:  	    result = Tcl_GetDoubleFromObj(interp, resultPtr, ptr); -	    if (result != TCL_OK) { -		return result; -	    }  	} -	Tcl_DecrRefCount(resultPtr);  /* discard the result object */      } +    Tcl_DecrRefCount(resultPtr);/* Discard the result object. */      return result;  }  int -Tcl_ExprBooleanObj(interp, objPtr, ptr) -    Tcl_Interp *interp;			/* Context in which to evaluate the -					 * expression. */ -    register Tcl_Obj *objPtr;		/* Expression to evaluate. */ -    int *ptr;				/* Where to store 0/1 result. */ +Tcl_ExprBooleanObj( +    Tcl_Interp *interp,		/* Context in which to evaluate the +				 * expression. */ +    register Tcl_Obj *objPtr,	/* Expression to evaluate. */ +    int *ptr)			/* Where to store 0/1 result. */  {      Tcl_Obj *resultPtr;      int result;      result = Tcl_ExprObj(interp, objPtr, &resultPtr);      if (result == TCL_OK) { -	if (resultPtr->typePtr == &tclIntType) { -	    *ptr = (resultPtr->internalRep.longValue != 0); -	} else if (resultPtr->typePtr == &tclDoubleType) { -	    *ptr = (resultPtr->internalRep.doubleValue != 0.0); -	} else { -	    result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); -	} -	Tcl_DecrRefCount(resultPtr);  /* discard the result object */ +	result = Tcl_GetBooleanFromObj(interp, resultPtr, ptr); +	Tcl_DecrRefCount(resultPtr); +				/* Discard the result object. */      }      return result;  } @@ -5346,12 +6477,14 @@ Tcl_ExprBooleanObj(interp, objPtr, ptr)  /*   *----------------------------------------------------------------------   * - * TclInvoke -- + * TclObjInvokeNamespace --   * - *	Invokes a Tcl command, given an argv/argc, from either the - *	exposed or the hidden sets of commands in the given interpreter. - *	NOTE: The command is invoked in the current stack frame of - *	the interpreter, thus it can modify local variables. + *	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.   *   * Results:   *	A standard Tcl result. @@ -5363,1142 +6496,2513 @@ Tcl_ExprBooleanObj(interp, objPtr, ptr)   */  int -TclInvoke(interp, argc, argv, flags) -    Tcl_Interp *interp;		/* Where to invoke the command. */ -    int argc;			/* Count of args. */ -    register CONST char **argv;	/* The arg strings; argv[0] is the name of -                                 * the command to invoke. */ -    int flags;			/* Combination of flags controlling the -				 * call: TCL_INVOKE_HIDDEN and -				 * TCL_INVOKE_NO_UNKNOWN. */ +TclObjInvokeNamespace( +    Tcl_Interp *interp,		/* Interpreter in which command is to be +				 * invoked. */ +    int objc,			/* Count of arguments. */ +    Tcl_Obj *const objv[],	/* Argument objects; objv[0] points to the +				 * name of the command to invoke. */ +    Tcl_Namespace *nsPtr,	/* The namespace to use. */ +    int flags)			/* Combination of flags controlling the call: +				 * TCL_INVOKE_HIDDEN, TCL_INVOKE_NO_UNKNOWN, +				 * or TCL_INVOKE_NO_TRACEBACK. */  { -    register Tcl_Obj *objPtr; -    register int i; -    int length, result; +    int result; +    Tcl_CallFrame *framePtr;      /* -     * This procedure generates an objv array for object arguments that hold -     * the argv strings. It starts out with stack-allocated space but uses -     * dynamically-allocated storage if needed. +     * Make the specified namespace the current namespace and invoke the +     * command.       */ -#define NUM_ARGS 20 -    Tcl_Obj *(objStorage[NUM_ARGS]); -    register Tcl_Obj **objv = objStorage; +    result = TclPushStackFrame(interp, &framePtr, nsPtr, /*isProcFrame*/0); +    if (result != TCL_OK) { +	return TCL_ERROR; +    } + +    result = TclObjInvoke(interp, objc, objv, flags); -    /* -     * Create the object argument array "objv". Make sure objv is large -     * enough to hold the objc arguments plus 1 extra for the zero -     * end-of-objv word. -     */ +    TclPopStackFrame(interp); +    return result; +} + +/* + *---------------------------------------------------------------------- + * + * TclObjInvoke -- + * + *	Invokes a Tcl command, given an objv/objc, from either the exposed or + *	the hidden sets of commands in the given interpreter. + * + * Results: + *	A standard Tcl object result. + * + * Side effects: + *	Whatever the command does. + * + *---------------------------------------------------------------------- + */ -    if ((argc + 1) > NUM_ARGS) { -	objv = (Tcl_Obj **) -	    ckalloc((unsigned)(argc + 1) * sizeof(Tcl_Obj *)); +int +TclObjInvoke( +    Tcl_Interp *interp,		/* Interpreter in which command is to be +				 * invoked. */ +    int objc,			/* Count of arguments. */ +    Tcl_Obj *const objv[],	/* Argument objects; objv[0] points to the +				 * 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. */ +{ +    if (interp == NULL) { +	return TCL_ERROR;      } +    if ((objc < 1) || (objv == NULL)) { +	Tcl_SetObjResult(interp, Tcl_NewStringObj( +                "illegal argument vector", -1)); +	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); +} -    for (i = 0;  i < argc;  i++) { -	length = strlen(argv[i]); -	objv[i] = Tcl_NewStringObj(argv[i], length); -	Tcl_IncrRefCount(objv[i]); +int +TclNRInvoke( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[]) +{ +    register 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; + +    cmdName = TclGetString(objv[0]); +    hTblPtr = iPtr->hiddenCmdTablePtr; +    if (hTblPtr != NULL) { +	hPtr = Tcl_FindHashEntry(hTblPtr, cmdName);      } -    objv[argc] = 0; +    if (hPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "invalid hidden command name \"%s\"", cmdName)); +        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "HIDDENTOKEN", cmdName, +                NULL); +	return TCL_ERROR; +    } +    cmdPtr = Tcl_GetHashValue(hPtr); + +    /* Avoid the exception-handling brain damage when numLevels == 0 . */ +    iPtr->numLevels++; +    Tcl_NRAddCallback(interp, NRPostInvoke, NULL, NULL, NULL, NULL);      /* -     * Use TclObjInterpProc to actually invoke the command. +     * 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).       */ -    result = TclObjInvoke(interp, argc, objv, flags); +    return TclNREvalObjv(interp, objc, objv, TCL_EVAL_NORESOLVE, cmdPtr); +} -    /* -     * Move the interpreter's object result to the string result,  -     * then reset the object result. -     */ -     -    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	    TCL_VOLATILE); +static int +NRPostInvoke( +    ClientData clientData[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *)interp; +    iPtr->numLevels--; +    return result; +} + +/* + *--------------------------------------------------------------------------- + * + * Tcl_ExprString -- + * + *	Evaluate an expression in a string and return its value in string + *	form. + * + * Results: + *	A standard Tcl result. If the result is TCL_OK, then the interp's + *	result is set to the string value of the expression. If the result is + *	TCL_ERROR, then the interp's result contains an error message. + * + * Side effects: + *	A Tcl object is allocated to hold a copy of the expression string. + *	This expression object is passed to Tcl_ExprObj and then deallocated. + * + *--------------------------------------------------------------------------- + */ -    /* -     * Decrement the ref counts on the objv elements since we are done -     * with them. -     */ +int +Tcl_ExprString( +    Tcl_Interp *interp,		/* Context in which to evaluate the +				 * expression. */ +    const char *expr)		/* Expression to evaluate. */ +{ +    int code = TCL_OK; -    for (i = 0;  i < argc;  i++) { -	objPtr = objv[i]; -	Tcl_DecrRefCount(objPtr); +    if (expr[0] == '\0') { +	/* +	 * An empty string. Just set the interpreter's result to 0. +	 */ + +	Tcl_SetObjResult(interp, Tcl_NewIntObj(0)); +    } else { +	Tcl_Obj *resultPtr, *exprObj = Tcl_NewStringObj(expr, -1); + +	Tcl_IncrRefCount(exprObj); +	code = Tcl_ExprObj(interp, exprObj, &resultPtr); +	Tcl_DecrRefCount(exprObj); +	if (code == TCL_OK) { +	    Tcl_SetObjResult(interp, resultPtr); +	    Tcl_DecrRefCount(resultPtr); +	}      } -     +      /* -     * Free the objv array if malloc'ed storage was used. +     * Force the string rep of the interp result.       */ -    if (objv != objStorage) { -	ckfree((char *) objv); -    } -    return result; -#undef NUM_ARGS +    (void) Tcl_GetStringResult(interp); +    return code;  }  /*   *----------------------------------------------------------------------   * - * TclGlobalInvoke -- + * Tcl_AppendObjToErrorInfo --   * - *	Invokes a Tcl command, given an argv/argc, from either the - *	exposed or hidden sets of commands in the given interpreter. - *	NOTE: The command is invoked in the global stack frame of - *	the interpreter, thus it cannot see any current state on - *	the stack for that interpreter. + *	Add a Tcl_Obj value to the errorInfo field that describes the current + *	error.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	Whatever the command does. + *	The value of the Tcl_obj is appended to the errorInfo field. If we are + *	just starting to log an error, errorInfo is initialized from the error + *	message in the interpreter's result.   *   *----------------------------------------------------------------------   */ -int -TclGlobalInvoke(interp, argc, argv, flags) -    Tcl_Interp *interp;		/* Where to invoke the command. */ -    int argc;			/* Count of args. */ -    register CONST char **argv;	/* The arg strings; argv[0] is the name of -                                 * the command to invoke. */ -    int flags;			/* Combination of flags controlling the -				 * call: TCL_INVOKE_HIDDEN and -				 * TCL_INVOKE_NO_UNKNOWN. */ +#undef Tcl_AddObjErrorInfo +void +Tcl_AppendObjToErrorInfo( +    Tcl_Interp *interp,		/* Interpreter to which error information +				 * pertains. */ +    Tcl_Obj *objPtr)		/* Message to record. */  { -    register Interp *iPtr = (Interp *) interp; -    int result; -    CallFrame *savedVarFramePtr; +    int length; +    const char *message = TclGetStringFromObj(objPtr, &length); -    savedVarFramePtr = iPtr->varFramePtr; -    iPtr->varFramePtr = NULL; -    result = TclInvoke(interp, argc, argv, flags); -    iPtr->varFramePtr = savedVarFramePtr; -    return result; +    Tcl_IncrRefCount(objPtr); +    Tcl_AddObjErrorInfo(interp, message, length); +    Tcl_DecrRefCount(objPtr);  }  /*   *----------------------------------------------------------------------   * - * TclObjInvokeGlobal -- + * Tcl_AddErrorInfo --   * - *	Object version: Invokes a Tcl command, given an objv/objc, from - *	either the exposed or hidden set of commands in the given - *	interpreter. - *	NOTE: The command is invoked in the global stack frame of the - *	interpreter, thus it cannot see any current state on the - *	stack of that interpreter. + *	Add information to the errorInfo field that describes the current + *	error.   *   * Results: - *	A standard Tcl result. + *	None.   *   * Side effects: - *	Whatever the command does. + *	The contents of message are appended to the errorInfo field. If we are + *	just starting to log an error, errorInfo is initialized from the error + *	message in the interpreter's result.   *   *----------------------------------------------------------------------   */ -int -TclObjInvokeGlobal(interp, objc, objv, flags) -    Tcl_Interp *interp;		/* Interpreter in which command is to be -				 * invoked. */ -    int objc;			/* Count of arguments. */ -    Tcl_Obj *CONST objv[];	/* Argument objects; objv[0] points to the -				 * 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. */ +#undef Tcl_AddErrorInfo +void +Tcl_AddErrorInfo( +    Tcl_Interp *interp,		/* Interpreter to which error information +				 * pertains. */ +    const char *message)	/* Message to record. */  { -    register Interp *iPtr = (Interp *) interp; -    int result; -    CallFrame *savedVarFramePtr; - -    savedVarFramePtr = iPtr->varFramePtr; -    iPtr->varFramePtr = NULL; -    result = TclObjInvoke(interp, objc, objv, flags); -    iPtr->varFramePtr = savedVarFramePtr; -    return result; +    Tcl_AddObjErrorInfo(interp, message, -1);  }  /*   *----------------------------------------------------------------------   * - * TclObjInvoke -- + * Tcl_AddObjErrorInfo --   * - *	Invokes a Tcl command, given an objv/objc, from either the - *	exposed or the hidden sets of commands in the given interpreter. + *	Add information to the errorInfo field that describes the current + *	error. This routine differs from Tcl_AddErrorInfo by taking a byte + *	pointer and length.   *   * Results: - *	A standard Tcl object result. + *	None.   *   * Side effects: - *	Whatever the command does. + *	"length" bytes from "message" are appended to the errorInfo field. If + *	"length" is negative, use bytes up to the first NULL byte. If we are + *	just starting to log an error, errorInfo is initialized from the error + *	message in the interpreter's result.   *   *----------------------------------------------------------------------   */ -int -TclObjInvoke(interp, objc, objv, flags) -    Tcl_Interp *interp;		/* Interpreter in which command is to be -				 * invoked. */ -    int objc;			/* Count of arguments. */ -    Tcl_Obj *CONST objv[];	/* Argument objects; objv[0] points to the -				 * 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. */ +void +Tcl_AddObjErrorInfo( +    Tcl_Interp *interp,		/* Interpreter to which error information +				 * pertains. */ +    const char *message,	/* Points to the first byte of an array of +				 * bytes of the message. */ +    int length)			/* The number of bytes in the message. If < 0, +				 * then append all bytes up to a NULL byte. */  {      register Interp *iPtr = (Interp *) interp; -    Tcl_HashTable *hTblPtr;	/* Table of hidden commands. */ -    char *cmdName;		/* Name of the command from objv[0]. */ -    register Tcl_HashEntry *hPtr; -    Tcl_Command cmd; -    Command *cmdPtr; -    int localObjc;		/* Used to invoke "unknown" if the */ -    Tcl_Obj **localObjv = NULL;	/* command is not found. */ -    register int i; -    int result; - -    if (interp == (Tcl_Interp *) NULL) { -        return TCL_ERROR; -    } - -    if ((objc < 1) || (objv == (Tcl_Obj **) NULL)) { -        Tcl_AppendToObj(Tcl_GetObjResult(interp), -	        "illegal argument vector", -1); -        return TCL_ERROR; -    } - -    cmdName = Tcl_GetString(objv[0]); -    if (flags & TCL_INVOKE_HIDDEN) { -        /* -         * We never invoke "unknown" for hidden commands. -         */ -         -	hPtr = NULL; -        hTblPtr = ((Interp *) interp)->hiddenCmdTablePtr; -        if (hTblPtr != NULL) { -	    hPtr = Tcl_FindHashEntry(hTblPtr, cmdName); -	} -	if (hPtr == NULL) { -	    Tcl_ResetResult(interp); -	    Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -		     "invalid hidden command name \"", cmdName, "\"", -		     (char *) NULL); -            return TCL_ERROR; -        } -	cmdPtr = (Command *) Tcl_GetHashValue(hPtr); -    } else { -	cmdPtr = NULL; -	cmd = Tcl_FindCommand(interp, cmdName, -	        (Tcl_Namespace *) NULL, /*flags*/ TCL_GLOBAL_ONLY); -        if (cmd != (Tcl_Command) NULL) { -	    cmdPtr = (Command *) cmd; -        } -	if (cmdPtr == NULL) { -            if (!(flags & TCL_INVOKE_NO_UNKNOWN)) { -		cmd = Tcl_FindCommand(interp, "unknown", -                        (Tcl_Namespace *) NULL, /*flags*/ TCL_GLOBAL_ONLY); -		if (cmd != (Tcl_Command) NULL) { -	            cmdPtr = (Command *) cmd; -                } -                if (cmdPtr != NULL) { -                    localObjc = (objc + 1); -                    localObjv = (Tcl_Obj **) -			ckalloc((unsigned) (sizeof(Tcl_Obj *) * localObjc)); -		    localObjv[0] = Tcl_NewStringObj("unknown", -1); -		    Tcl_IncrRefCount(localObjv[0]); -                    for (i = 0;  i < objc;  i++) { -                        localObjv[i+1] = objv[i]; -                    } -                    objc = localObjc; -                    objv = localObjv; -                } -            } - -            /* -             * Check again if we found the command. If not, "unknown" is -             * not present and we cannot help, or the caller said not to -             * call "unknown" (they specified TCL_INVOKE_NO_UNKNOWN). -             */ - -            if (cmdPtr == NULL) { -		Tcl_ResetResult(interp); -		Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), -			"invalid command name \"",  cmdName, "\"",  -			 (char *) NULL); -                return TCL_ERROR; -            } -        } -    } - -    /* -     * Invoke the command procedure. First reset the interpreter's string -     * and object results to their default empty values since they could -     * have gotten changed by earlier invocations. -     */ - -    Tcl_ResetResult(interp); -    iPtr->cmdCount++; -    result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp, objc, objv);      /* -     * If an error occurred, record information about what was being -     * executed when the error occurred. +     * If we are just starting to log an error, errorInfo is initialized from +     * the error message in the interpreter's result.       */ -    if ((result == TCL_ERROR) -	    && ((flags & TCL_INVOKE_NO_TRACEBACK) == 0) -	    && ((iPtr->flags & ERR_ALREADY_LOGGED) == 0)) { -	Tcl_Obj *msg; -         -        if (!(iPtr->flags & ERR_IN_PROGRESS)) { -            msg = Tcl_NewStringObj("\n    while invoking\n\"", -1); -        } else { -            msg = Tcl_NewStringObj("\n    invoked from within\n\"", -1); -        } -	Tcl_IncrRefCount(msg); -        for (i = 0;  i < objc;  i++) { -	    CONST char *bytes; -	    int length; - -	    Tcl_AppendObjToObj(msg, objv[i]); -	    bytes = Tcl_GetStringFromObj(msg, &length); -	    if (length > 100) { -		/* -		 * Back up truncation point so that we don't truncate -		 * in the middle of a multi-byte character. -		 */ -		length = 100; -		while ( (bytes[length] & 0xC0) == 0x80 ) { -		    length--; -		} -		Tcl_SetObjLength(msg, length); -		Tcl_AppendToObj(msg, "...", -1); -		break; -	    } -	    if (i != (objc - 1)) { -		Tcl_AppendToObj(msg, " ", -1); -	    } -        } +    iPtr->flags |= ERR_LEGACY_COPY; +    if (iPtr->errorInfo == NULL) { +	if (iPtr->result[0] != 0) { +	    /* +	     * The interp's string result is set, apparently by some extension +	     * making a deprecated direct write to it. That extension may +	     * expect interp->result to continue to be set, so we'll take +	     * special pains to avoid clearing it, until we drop support for +	     * interp->result completely. +	     */ -	Tcl_AppendToObj(msg, "\"", -1); -        Tcl_AddObjErrorInfo(interp, Tcl_GetString(msg), -1); -	Tcl_DecrRefCount(msg); -	iPtr->flags &= ~ERR_ALREADY_LOGGED; +	    iPtr->errorInfo = Tcl_NewStringObj(iPtr->result, -1); +	} else { +	    iPtr->errorInfo = iPtr->objResultPtr; +	} +	Tcl_IncrRefCount(iPtr->errorInfo); +	if (!iPtr->errorCode) { +	    Tcl_SetErrorCode(interp, "NONE", NULL); +	}      }      /* -     * Free any locally allocated storage used to call "unknown". +     * Now append "message" to the end of errorInfo.       */ -    if (localObjv != (Tcl_Obj **) NULL) { -	Tcl_DecrRefCount(localObjv[0]); -        ckfree((char *) localObjv); +    if (length != 0) { +	if (Tcl_IsShared(iPtr->errorInfo)) { +	    Tcl_DecrRefCount(iPtr->errorInfo); +	    iPtr->errorInfo = Tcl_DuplicateObj(iPtr->errorInfo); +	    Tcl_IncrRefCount(iPtr->errorInfo); +	} +	Tcl_AppendToObj(iPtr->errorInfo, message, length);      } -    return result;  }  /*   *---------------------------------------------------------------------------   * - * Tcl_ExprString -- + * Tcl_VarEvalVA --   * - *	Evaluate an expression in a string and return its value in string - *	form. + *	Given a variable number of string arguments, concatenate them all + *	together and execute the result as a Tcl command.   *   * Results: - *	A standard Tcl result. If the result is TCL_OK, then the interp's - *	result is set to the string value of the expression. If the result - *	is TCL_ERROR, then the interp's result contains an error message. + *	A standard Tcl return result. An error message or other result may be + *	left in the interp's result.   *   * Side effects: - *	A Tcl object is allocated to hold a copy of the expression string. - *	This expression object is passed to Tcl_ExprObj and then - *	deallocated. + *	Depends on what was done by the command.   *   *---------------------------------------------------------------------------   */  int -Tcl_ExprString(interp, string) -    Tcl_Interp *interp;		/* Context in which to evaluate the -				 * expression. */ -    CONST char *string;		/* Expression to evaluate. */ +Tcl_VarEvalVA( +    Tcl_Interp *interp,		/* Interpreter in which to evaluate command */ +    va_list argList)		/* Variable argument list. */  { -    register Tcl_Obj *exprPtr; -    Tcl_Obj *resultPtr; -    int length = strlen(string); -    char buf[TCL_DOUBLE_SPACE]; -    int result = TCL_OK; +    Tcl_DString buf; +    char *string; +    int result; -    if (length > 0) { -	TclNewObj(exprPtr); -	TclInitStringRep(exprPtr, string, length); -	Tcl_IncrRefCount(exprPtr); +    /* +     * Copy the strings one after the other into a single larger string. Use +     * stack-allocated space for small commands, but if the command gets too +     * large than call ckalloc to create the space. +     */ -	result = Tcl_ExprObj(interp, exprPtr, &resultPtr); -	if (result == TCL_OK) { -	    /* -	     * Set the interpreter's string result from the result object. -	     */ -	     -	    if (resultPtr->typePtr == &tclIntType) { -		sprintf(buf, "%ld", resultPtr->internalRep.longValue); -		Tcl_SetResult(interp, buf, TCL_VOLATILE); -	    } else if (resultPtr->typePtr == &tclDoubleType) { -		Tcl_PrintDouble((Tcl_Interp *) NULL, -		        resultPtr->internalRep.doubleValue, buf); -		Tcl_SetResult(interp, buf, TCL_VOLATILE); -	    } else { -		/* -		 * Set interpreter's string result from the result object. -		 */ -	     -		Tcl_SetResult(interp, TclGetString(resultPtr), -		        TCL_VOLATILE); -	    } -	    Tcl_DecrRefCount(resultPtr);  /* discard the result object */ -	} else { -	    /* -	     * Move the interpreter's object result to the string result,  -	     * then reset the object result. -	     */ -	     -	    Tcl_SetResult(interp, TclGetString(Tcl_GetObjResult(interp)), -	            TCL_VOLATILE); +    Tcl_DStringInit(&buf); +    while (1) { +	string = va_arg(argList, char *); +	if (string == NULL) { +	    break;  	} -	Tcl_DecrRefCount(exprPtr); /* discard the expression object */ -    } else { -	/* -	 * An empty string. Just set the interpreter's result to 0. -	 */ -	 -	Tcl_SetResult(interp, "0", TCL_VOLATILE); +	Tcl_DStringAppend(&buf, string, -1);      } + +    result = Tcl_Eval(interp, Tcl_DStringValue(&buf)); +    Tcl_DStringFree(&buf);      return result;  }  /*   *----------------------------------------------------------------------   * - * Tcl_CreateObjTrace -- + * Tcl_VarEval --   * - *	Arrange for a procedure to be called to trace command execution. + *	Given a variable number of string arguments, concatenate them all + *	together and execute the result as a Tcl command.   *   * Results: - *	The return value is a token for the trace, which may be passed - *	to Tcl_DeleteTrace to eliminate the trace. + *	A standard Tcl return result. An error message or other result may be + *	left in interp->result.   *   * Side effects: - *	From now on, proc will be called just before a command procedure - *	is called to execute a Tcl command.  Calls to proc will have the - *	following form: - * - *      void proc( ClientData     clientData, - *                 Tcl_Interp*    interp, - *                 int            level, - *                 CONST char*    command, - *                 Tcl_Command    commandInfo, - *                 int            objc, - *                 Tcl_Obj *CONST objv[] ); - * - *      The 'clientData' and 'interp' arguments to 'proc' will be the - *      same as the arguments to Tcl_CreateObjTrace.  The 'level' - *	argument gives the nesting depth of command interpretation within - *	the interpreter.  The 'command' argument is the ASCII text of - *	the command being evaluated -- before any substitutions are - *	performed.  The 'commandInfo' argument gives a handle to the - *	command procedure that will be evaluated.  The 'objc' and 'objv' - *	parameters give the parameter vector that will be passed to the - *	command procedure.  proc does not return a value. - * - *      It is permissible for 'proc' to call Tcl_SetCommandTokenInfo - *      to change the command procedure or client data for the command - *      being evaluated, and these changes will take effect with the - *      current evaluation. - * - * The 'level' argument specifies the maximum nesting level of calls - * to be traced.  If the execution depth of the interpreter exceeds - * 'level', the trace callback is not executed. - * - * The 'flags' argument is either zero or the value, - * TCL_ALLOW_INLINE_COMPILATION.  If the TCL_ALLOW_INLINE_COMPILATION - * flag is not present, the bytecode compiler will not generate inline - * code for Tcl's built-in commands.  This behavior will have a significant - * impact on performance, but will ensure that all command evaluations are - * traced.  If the TCL_ALLOW_INLINE_COMPILATION flag is present, the - * bytecode compiler will have its normal behavior of compiling in-line - * code for some of Tcl's built-in commands.  In this case, the tracing - * will be imprecise -- in-line code will not be traced -- but run-time - * performance will be improved.  The latter behavior is desired for - * many applications such as profiling of run time. - * - * When the trace is deleted, the 'delProc' procedure will be invoked, - * passing it the original client data.   + *	Depends on what was done by the command.   *   *----------------------------------------------------------------------   */ +	/* ARGSUSED */ +int +Tcl_VarEval( +    Tcl_Interp *interp, +    ...) +{ +    va_list argList; +    int result; -Tcl_Trace -Tcl_CreateObjTrace( interp, level, flags, proc, clientData, delProc ) -    Tcl_Interp* interp;		/* Tcl interpreter */ -    int level;			/* Maximum nesting level */ -    int flags;			/* Flags, see above */ -    Tcl_CmdObjTraceProc* proc;	/* Trace callback */ -    ClientData clientData;	/* Client data for the callback */ -    Tcl_CmdObjTraceDeleteProc* delProc; -				/* Procedure to call when trace is deleted */ -{ -    register Trace *tracePtr; -    register Interp *iPtr = (Interp *) interp; - -    /* Test if this trace allows inline compilation of commands */ - -    if (!(flags & TCL_ALLOW_INLINE_COMPILATION)) { -	if (iPtr->tracesForbiddingInline == 0) { - -	    /* -	     * When the first trace forbidding inline compilation is -	     * created, invalidate existing compiled code for this -	     * interpreter and arrange (by setting the -	     * DONT_COMPILE_CMDS_INLINE flag) that when compiling new -	     * code, no commands will be compiled inline (i.e., into -	     * an inline sequence of instructions). We do this because -	     * commands that were compiled inline will never result in -	     * a command trace being called. -	     */ - -	    iPtr->compileEpoch++; -	    iPtr->flags |= DONT_COMPILE_CMDS_INLINE; -	} -	iPtr->tracesForbiddingInline++; -    } -     -    tracePtr = (Trace *) ckalloc(sizeof(Trace)); -    tracePtr->level		= level; -    tracePtr->proc		= proc; -    tracePtr->clientData	= clientData; -    tracePtr->delProc           = delProc; -    tracePtr->nextPtr		= iPtr->tracePtr; -    tracePtr->flags		= flags; -    iPtr->tracePtr		= tracePtr; +    va_start(argList, interp); +    result = Tcl_VarEvalVA(interp, argList); +    va_end(argList); -    return (Tcl_Trace) tracePtr; +    return result;  }  /*   *----------------------------------------------------------------------   * - * Tcl_CreateTrace -- + * Tcl_GlobalEval --   * - *	Arrange for a procedure to be called to trace command execution. + *	Evaluate a command at global level in an interpreter.   *   * Results: - *	The return value is a token for the trace, which may be passed - *	to Tcl_DeleteTrace to eliminate the trace. + *	A standard Tcl result is returned, and the interp's result is modified + *	accordingly.   *   * Side effects: - *	From now on, proc will be called just before a command procedure - *	is called to execute a Tcl command.  Calls to proc will have the - *	following form: - * - *	void - *	proc(clientData, interp, level, command, cmdProc, cmdClientData, - *		argc, argv) - *	    ClientData clientData; - *	    Tcl_Interp *interp; - *	    int level; - *	    char *command; - *	    int (*cmdProc)(); - *	    ClientData cmdClientData; - *	    int argc; - *	    char **argv; - *	{ - *	} - * - *	The clientData and interp arguments to proc will be the same - *	as the corresponding arguments to this procedure.  Level gives - *	the nesting level of command interpretation for this interpreter - *	(0 corresponds to top level).  Command gives the ASCII text of - *	the raw command, cmdProc and cmdClientData give the procedure that - *	will be called to process the command and the ClientData value it - *	will receive, and argc and argv give the arguments to the - *	command, after any argument parsing and substitution.  Proc - *	does not return a value. + *	The command string is executed in interp, and the execution is carried + *	out in the variable context of global level (no functions active), + *	just as if an "uplevel #0" command were being executed.   *   *----------------------------------------------------------------------   */ -Tcl_Trace -Tcl_CreateTrace(interp, level, proc, clientData) -    Tcl_Interp *interp;		/* Interpreter in which to create trace. */ -    int level;			/* Only call proc for commands at nesting -				 * level<=argument level (1=>top level). */ -    Tcl_CmdTraceProc *proc;	/* Procedure to call before executing each +#undef Tcl_GlobalEval +int +Tcl_GlobalEval( +    Tcl_Interp *interp,		/* Interpreter in which to evaluate  				 * command. */ -    ClientData clientData;	/* Arbitrary value word to pass to proc. */ +    const char *command)	/* Command to evaluate. */  { -    StringTraceData* data; -    data = (StringTraceData*) ckalloc( sizeof( *data )); -    data->clientData = clientData; -    data->proc = proc; -    return Tcl_CreateObjTrace( interp, level, 0, StringTraceProc, -			       (ClientData) data, StringTraceDeleteProc ); +    register Interp *iPtr = (Interp *) interp; +    int result; +    CallFrame *savedVarFramePtr; + +    savedVarFramePtr = iPtr->varFramePtr; +    iPtr->varFramePtr = iPtr->rootFramePtr; +    result = Tcl_Eval(interp, command); +    iPtr->varFramePtr = savedVarFramePtr; +    return result;  }  /*   *----------------------------------------------------------------------   * - * StringTraceProc -- + * Tcl_SetRecursionLimit --   * - *	Invoke a string-based trace procedure from an object-based - *	callback. + *	Set the maximum number of recursive calls that may be active for an + *	interpreter at once.   *   * Results: - *	None. + *	The return value is the old limit on nesting for interp.   *   * Side effects: - *	Whatever the string-based trace procedure does. + *	None.   *   *----------------------------------------------------------------------   */ -static int -StringTraceProc( clientData, interp, level, command, commandInfo, objc, objv ) -    ClientData clientData; -    Tcl_Interp* interp; -    int level; -    CONST char* command; -    Tcl_Command commandInfo; -    int objc; -    Tcl_Obj *CONST *objv; +int +Tcl_SetRecursionLimit( +    Tcl_Interp *interp,		/* Interpreter whose nesting limit is to be +				 * set. */ +    int depth)			/* New value for maximimum depth. */  { -    StringTraceData* data = (StringTraceData*) clientData; -    Command* cmdPtr = (Command*) commandInfo; - -    CONST char** argv;		/* Args to pass to string trace proc */ - -    int i; +    Interp *iPtr = (Interp *) interp; +    int old; -    /* -     * This is a bit messy because we have to emulate the old trace -     * interface, which uses strings for everything. -     */ -	     -    argv = (CONST char **) ckalloc((unsigned) ( (objc + 1) -						* sizeof(CONST char *) )); -    for (i = 0; i < objc; i++) { -	argv[i] = Tcl_GetString(objv[i]); +    old = iPtr->maxNestingDepth; +    if (depth > 0) { +	iPtr->maxNestingDepth = depth;      } -    argv[objc] = 0; - -    /* -     * Invoke the command procedure.  Note that we cast away const-ness -     * on two parameters for compatibility with legacy code; the code -     * MUST NOT modify either command or argv. -     */ -           -    ( data->proc )( data->clientData, interp, level, -		    (char*) command, cmdPtr->proc, cmdPtr->clientData, -		    objc, argv ); -    ckfree( (char*) argv ); - -    return TCL_OK; +    return old;  }  /*   *----------------------------------------------------------------------   * - * StringTraceDeleteProc -- + * Tcl_AllowExceptions --   * - *	Clean up memory when a string-based trace is deleted. + *	Sets a flag in an interpreter so that exceptions can occur in the next + *	call to Tcl_Eval without them being turned into errors.   *   * Results:   *	None.   *   * Side effects: - *	Allocated memory is returned to the system. + *	The TCL_ALLOW_EXCEPTIONS flag gets set in the interpreter's evalFlags + *	structure. See the reference documentation for more details.   *   *----------------------------------------------------------------------   */ -static void -StringTraceDeleteProc( clientData ) -    ClientData clientData; +void +Tcl_AllowExceptions( +    Tcl_Interp *interp)		/* Interpreter in which to set flag. */  { -    ckfree( (char*) clientData ); +    Interp *iPtr = (Interp *) interp; + +    iPtr->evalFlags |= TCL_ALLOW_EXCEPTIONS;  }  /*   *----------------------------------------------------------------------   * - * Tcl_DeleteTrace -- + * Tcl_GetVersion --   * - *	Remove a trace. + *	Get the Tcl major, minor, and patchlevel version numbers and the + *	release type. A patch is a release type TCL_FINAL_RELEASE with a + *	patchLevel > 0.   *   * Results:   *	None.   *   * Side effects: - *	From now on there will be no more calls to the procedure given - *	in trace. + *	None.   *   *----------------------------------------------------------------------   */  void -Tcl_DeleteTrace(interp, trace) -    Tcl_Interp *interp;		/* Interpreter that contains trace. */ -    Tcl_Trace trace;		/* Token for trace (returned previously by -				 * Tcl_CreateTrace). */ +Tcl_GetVersion( +    int *majorV, +    int *minorV, +    int *patchLevelV, +    int *type)  { -    Interp *iPtr = (Interp *) interp; -    Trace *prevPtr, *tracePtr = (Trace *) trace; -    register Trace **tracePtr2 = &(iPtr->tracePtr); -    ActiveInterpTrace *activePtr; +    if (majorV != NULL) { +	*majorV = TCL_MAJOR_VERSION; +    } +    if (minorV != NULL) { +	*minorV = TCL_MINOR_VERSION; +    } +    if (patchLevelV != NULL) { +	*patchLevelV = TCL_RELEASE_SERIAL; +    } +    if (type != NULL) { +	*type = TCL_RELEASE_LEVEL; +    } +} + +/* + *---------------------------------------------------------------------- + * + * Math Functions -- + * + *	This page contains the functions that implement all of the built-in + *	math functions for expressions. + * + * Results: + *	Each function returns TCL_OK if it succeeds and pushes an Tcl object + *	holding the result. If it fails it returns TCL_ERROR and leaves an + *	error message in the interpreter's result. + * + * Side effects: + *	None. + * + *---------------------------------------------------------------------- + */ + +static int +ExprCeilFunc( +    ClientData clientData,	/* Ignored */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter list. */ +{ +    int code; +    double d; +    mp_int big; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN +    if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { +	Tcl_SetObjResult(interp, objv[1]); +	return TCL_OK; +    } +#endif +    if (code != TCL_OK) { +	return TCL_ERROR; +    } + +    if (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK) { +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclCeil(&big))); +	mp_clear(&big); +    } else { +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(ceil(d))); +    } +    return TCL_OK; +} + +static int +ExprFloorFunc( +    ClientData clientData,	/* Ignored */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter list. */ +{ +    int code; +    double d; +    mp_int big; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN +    if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { +	Tcl_SetObjResult(interp, objv[1]); +	return TCL_OK; +    } +#endif +    if (code != TCL_OK) { +	return TCL_ERROR; +    } + +    if (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK) { +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclFloor(&big))); +	mp_clear(&big); +    } else { +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(floor(d))); +    } +    return TCL_OK; +} + +static int +ExprIsqrtFunc( +    ClientData clientData,	/* Ignored */ +    Tcl_Interp *interp,		/* The interpreter in which to execute. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter list. */ +{ +    ClientData ptr; +    int type; +    double d; +    Tcl_WideInt w; +    mp_int big; +    int exact = 0;		/* Flag ==1 if the argument can be represented +				 * in a double as an exact integer. */      /* -     * Locate the trace entry in the interpreter's trace list, -     * and remove it from the list. +     * Check syntax.       */ -    prevPtr = NULL; -    while ((*tracePtr2) != NULL && (*tracePtr2) != tracePtr) { -	prevPtr = *tracePtr2; -	tracePtr2 = &((*tracePtr2)->nextPtr); +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR;      } -    if (*tracePtr2 == NULL) { -	return; + +    /* +     * Make sure that the arg is a number. +     */ + +    if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { +	return TCL_ERROR; +    } + +    switch (type) { +    case TCL_NUMBER_NAN: +	Tcl_GetDoubleFromObj(interp, objv[1], &d); +	return TCL_ERROR; +    case TCL_NUMBER_DOUBLE: +	d = *((const double *) ptr); +	if (d < 0) { +	    goto negarg; +	} +#ifdef IEEE_FLOATING_POINT +	if (d <= MAX_EXACT) { +	    exact = 1; +	} +#endif +	if (!exact) { +	    if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { +		return TCL_ERROR; +	    } +	} +	break; +    case TCL_NUMBER_BIG: +	if (Tcl_GetBignumFromObj(interp, objv[1], &big) != TCL_OK) { +	    return TCL_ERROR; +	} +	if (SIGN(&big) == MP_NEG) { +	    mp_clear(&big); +	    goto negarg; +	} +	break; +    default: +	if (Tcl_GetWideIntFromObj(interp, objv[1], &w) != TCL_OK) { +	    return TCL_ERROR; +	} +	if (w < 0) { +	    goto negarg; +	} +	d = (double) w; +#ifdef IEEE_FLOATING_POINT +	if (d < MAX_EXACT) { +	    exact = 1; +	} +#endif +	if (!exact) { +	    Tcl_GetBignumFromObj(interp, objv[1], &big); +	} +	break; +    } + +    if (exact) { +	Tcl_SetObjResult(interp, Tcl_NewWideIntObj((Tcl_WideInt) sqrt(d))); +    } else { +	mp_int root; + +	mp_init(&root); +	mp_sqrt(&big, &root); +	mp_clear(&big); +	Tcl_SetObjResult(interp, Tcl_NewBignumObj(&root)); +    } +    return TCL_OK; + +  negarg: +    Tcl_SetObjResult(interp, Tcl_NewStringObj( +            "square root of negative argument", -1)); +    Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", +	    "domain error: argument not in valid range", NULL); +    return TCL_ERROR; +} + +static int +ExprSqrtFunc( +    ClientData clientData,	/* Ignored */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter list. */ +{ +    int code; +    double d; +    mp_int big; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN +    if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { +	Tcl_SetObjResult(interp, objv[1]); +	return TCL_OK; +    } +#endif +    if (code != TCL_OK) { +	return TCL_ERROR; +    } +    if ((d >= 0.0) && TclIsInfinite(d) +	    && (Tcl_GetBignumFromObj(NULL, objv[1], &big) == TCL_OK)) { +	mp_int root; + +	mp_init(&root); +	mp_sqrt(&big, &root); +	mp_clear(&big); +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(TclBignumToDouble(&root))); +	mp_clear(&root); +    } else { +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(sqrt(d))); +    } +    return TCL_OK; +} + +static int +ExprUnaryFunc( +    ClientData clientData,	/* Contains the address of a function that +				 * takes one double argument and returns a +				 * double result. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count */ +    Tcl_Obj *const *objv)	/* Actual parameter list */ +{ +    int code; +    double d; +    double (*func)(double) = (double (*)(double)) clientData; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    code = Tcl_GetDoubleFromObj(interp, objv[1], &d); +#ifdef ACCEPT_NAN +    if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { +	d = objv[1]->internalRep.doubleValue; +	Tcl_ResetResult(interp); +	code = TCL_OK; +    } +#endif +    if (code != TCL_OK) { +	return TCL_ERROR; +    } +    errno = 0; +    return CheckDoubleResult(interp, func(d)); +} + +static int +CheckDoubleResult( +    Tcl_Interp *interp, +    double dResult) +{ +#ifndef ACCEPT_NAN +    if (TclIsNaN(dResult)) { +	TclExprFloatError(interp, dResult); +	return TCL_ERROR; +    } +#endif +    if ((errno == ERANGE) && ((dResult == 0.0) || TclIsInfinite(dResult))) { +	/* +	 * When ERANGE signals under/overflow, just accept 0.0 or +/-Inf +	 */ +    } else if (errno != 0) { +	/* +	 * Report other errno values as errors. +	 */ + +	TclExprFloatError(interp, dResult); +	return TCL_ERROR; +    } +    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dResult)); +    return TCL_OK; +} + +static int +ExprBinaryFunc( +    ClientData clientData,	/* Contains the address of a function that +				 * takes two double arguments and returns a +				 * double result. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Parameter vector. */ +{ +    int code; +    double d1, d2; +    double (*func)(double, double) = (double (*)(double, double)) clientData; + +    if (objc != 3) { +	MathFuncWrongNumArgs(interp, 3, objc, objv); +	return TCL_ERROR; +    } +    code = Tcl_GetDoubleFromObj(interp, objv[1], &d1); +#ifdef ACCEPT_NAN +    if ((code != TCL_OK) && (objv[1]->typePtr == &tclDoubleType)) { +	d1 = objv[1]->internalRep.doubleValue; +	Tcl_ResetResult(interp); +	code = TCL_OK; +    } +#endif +    if (code != TCL_OK) { +	return TCL_ERROR; +    } +    code = Tcl_GetDoubleFromObj(interp, objv[2], &d2); +#ifdef ACCEPT_NAN +    if ((code != TCL_OK) && (objv[2]->typePtr == &tclDoubleType)) { +	d2 = objv[2]->internalRep.doubleValue; +	Tcl_ResetResult(interp); +	code = TCL_OK; +    } +#endif +    if (code != TCL_OK) { +	return TCL_ERROR; +    } +    errno = 0; +    return CheckDoubleResult(interp, func(d1, d2)); +} + +static int +ExprAbsFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Parameter vector. */ +{ +    ClientData ptr; +    int type; +    mp_int big; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } + +    if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { +	return TCL_ERROR; +    } + +    if (type == TCL_NUMBER_LONG) { +	long l = *((const long *) ptr); + +	if (l > (long)0) { +	    goto unChanged; +	} else if (l == (long)0) { +	    const char *string = objv[1]->bytes; +	    if (string) { +		while (*string != '0') { +		    if (*string == '-') { +			Tcl_SetObjResult(interp, Tcl_NewLongObj(0)); +			return TCL_OK; +		    } +		    string++; +		} +	    } +	    goto unChanged; +	} else if (l == LONG_MIN) { +	    TclBNInitBignumFromLong(&big, l); +	    goto tooLarge; +	} +	Tcl_SetObjResult(interp, Tcl_NewLongObj(-l)); +	return TCL_OK; +    } + +    if (type == TCL_NUMBER_DOUBLE) { +	double d = *((const double *) ptr); +	static const double poszero = 0.0; + +	/* +	 * We need to distinguish here between positive 0.0 and negative -0.0. +	 * [Bug 2954959] +	 */ + +	if (d == -0.0) { +	    if (!memcmp(&d, &poszero, sizeof(double))) { +		goto unChanged; +	    } +	} else if (d > -0.0) { +	    goto unChanged; +	} +	Tcl_SetObjResult(interp, Tcl_NewDoubleObj(-d)); +	return TCL_OK; +    } + +#ifndef TCL_WIDE_INT_IS_LONG +    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_cmp_d((const mp_int *) ptr, 0) == MP_LT) { +	    Tcl_GetBignumFromObj(NULL, objv[1], &big); +	tooLarge: +	    mp_neg(&big, &big); +	    Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); +	} else { +	unChanged: +	    Tcl_SetObjResult(interp, objv[1]); +	} +	return TCL_OK; +    } + +    if (type == TCL_NUMBER_NAN) { +#ifdef ACCEPT_NAN +	Tcl_SetObjResult(interp, objv[1]); +	return TCL_OK; +#else +	double d; + +	Tcl_GetDoubleFromObj(interp, objv[1], &d); +	return TCL_ERROR; +#endif +    } +    return TCL_OK; +} + +static int +ExprBoolFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */ +{ +    int value; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    if (Tcl_GetBooleanFromObj(interp, objv[1], &value) != TCL_OK) { +	return TCL_ERROR; +    } +    Tcl_SetObjResult(interp, Tcl_NewBooleanObj(value)); +    return TCL_OK; +} + +static int +ExprDoubleFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */ +{ +    double dResult; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    if (Tcl_GetDoubleFromObj(interp, objv[1], &dResult) != TCL_OK) { +#ifdef ACCEPT_NAN +	if (objv[1]->typePtr == &tclDoubleType) { +	    Tcl_SetObjResult(interp, objv[1]); +	    return TCL_OK; +	} +#endif +	return TCL_ERROR; +    } +    Tcl_SetObjResult(interp, Tcl_NewDoubleObj(dResult)); +    return TCL_OK; +} + +static int +ExprEntierFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */ +{ +    double d; +    int type; +    ClientData ptr; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } +    if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { +	return TCL_ERROR; +    } + +    if (type == TCL_NUMBER_DOUBLE) { +	d = *((const double *) ptr); +	if ((d >= (double)LONG_MAX) || (d <= (double)LONG_MIN)) { +	    mp_int big; + +	    if (Tcl_InitBignumFromDouble(interp, d, &big) != TCL_OK) { +		/* Infinity */ +		return TCL_ERROR; +	    } +	    Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); +	    return TCL_OK; +	} else { +	    long result = (long) d; + +	    Tcl_SetObjResult(interp, Tcl_NewLongObj(result)); +	    return TCL_OK; +	} +    } + +    if (type != TCL_NUMBER_NAN) { +	/* +	 * All integers are already of integer type. +	 */ + +	Tcl_SetObjResult(interp, objv[1]); +	return TCL_OK;      } -    (*tracePtr2) = (*tracePtr2)->nextPtr;      /* -     * The code below makes it possible to delete traces while traces -     * are active: it makes sure that the deleted trace won't be -     * processed by TclCheckInterpTraces. +     * Get the error message for NaN.       */ -    for (activePtr = iPtr->activeInterpTracePtr;  activePtr != NULL; -	    activePtr = activePtr->nextPtr) { -	if (activePtr->nextTracePtr == tracePtr) { -	    if (activePtr->reverseScan) { -		activePtr->nextTracePtr = prevPtr; -	    } else { -		activePtr->nextTracePtr = tracePtr->nextPtr; +    Tcl_GetDoubleFromObj(interp, objv[1], &d); +    return TCL_ERROR; +} + +static int +ExprIntFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */ +{ +    long iResult; +    Tcl_Obj *objPtr; +    if (ExprEntierFunc(NULL, interp, objc, objv) != TCL_OK) { +	return TCL_ERROR; +    } +    objPtr = Tcl_GetObjResult(interp); +    if (TclGetLongFromObj(NULL, objPtr, &iResult) != TCL_OK) { +	/* +	 * Truncate the bignum; keep only bits in long range. +	 */ + +	mp_int big; + +	Tcl_GetBignumFromObj(NULL, objPtr, &big); +	mp_mod_2d(&big, (int) CHAR_BIT * sizeof(long), &big); +	objPtr = Tcl_NewBignumObj(&big); +	Tcl_IncrRefCount(objPtr); +	TclGetLongFromObj(NULL, objPtr, &iResult); +	Tcl_DecrRefCount(objPtr); +    } +    Tcl_SetObjResult(interp, Tcl_NewLongObj(iResult)); +    return TCL_OK; +} + +static int +ExprWideFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */ +{ +    Tcl_WideInt wResult; +    Tcl_Obj *objPtr; + +    if (ExprEntierFunc(NULL, interp, objc, objv) != TCL_OK) { +	return TCL_ERROR; +    } +    objPtr = Tcl_GetObjResult(interp); +    if (Tcl_GetWideIntFromObj(NULL, objPtr, &wResult) != TCL_OK) { +	/* +	 * Truncate the bignum; keep only bits in wide int range. +	 */ + +	mp_int big; + +	Tcl_GetBignumFromObj(NULL, objPtr, &big); +	mp_mod_2d(&big, (int) CHAR_BIT * sizeof(Tcl_WideInt), &big); +	objPtr = Tcl_NewBignumObj(&big); +	Tcl_IncrRefCount(objPtr); +	Tcl_GetWideIntFromObj(NULL, objPtr, &wResult); +	Tcl_DecrRefCount(objPtr); +    } +    Tcl_SetObjResult(interp, Tcl_NewWideIntObj(wResult)); +    return TCL_OK; +} + +static int +ExprRandFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */ +{ +    Interp *iPtr = (Interp *) interp; +    double dResult; +    long tmp;			/* Algorithm assumes at least 32 bits. Only +				 * long guarantees that. See below. */ +    Tcl_Obj *oResult; + +    if (objc != 1) { +	MathFuncWrongNumArgs(interp, 1, objc, objv); +	return TCL_ERROR; +    } + +    if (!(iPtr->flags & RAND_SEED_INITIALIZED)) { +	iPtr->flags |= RAND_SEED_INITIALIZED; + +	/* +	 * Take into consideration the thread this interp is running in order +	 * to insure different seeds in different threads (bug #416643) +	 */ + +	iPtr->randSeed = TclpGetClicks() + (PTR2INT(Tcl_GetCurrentThread())<<12); + +	/* +	 * Make sure 1 <= randSeed <= (2^31) - 2. See below. +	 */ + +	iPtr->randSeed &= (unsigned long) 0x7fffffff; +	if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { +	    iPtr->randSeed ^= 123459876; +	} +    } + +    /* +     * Generate the random number using the linear congruential generator +     * defined by the following recurrence: +     *		seed = ( IA * seed ) mod IM +     * where IA is 16807 and IM is (2^31) - 1. The recurrence maps a seed in +     * the range [1, IM - 1] to a new seed in that same range. The recurrence +     * maps IM to 0, and maps 0 back to 0, so those two values must not be +     * allowed as initial values of seed. +     * +     * In order to avoid potential problems with integer overflow, the +     * recurrence is implemented in terms of additional constants IQ and IR +     * such that +     *		IM = IA*IQ + IR +     * None of the operations in the implementation overflows a 32-bit signed +     * integer, and the C type long is guaranteed to be at least 32 bits wide. +     * +     * For more details on how this algorithm works, refer to the following +     * papers: +     * +     *	S.K. Park & K.W. Miller, "Random number generators: good ones are hard +     *	to find," Comm ACM 31(10):1192-1201, Oct 1988 +     * +     *	W.H. Press & S.A. Teukolsky, "Portable random number generators," +     *	Computers in Physics 6(5):522-524, Sep/Oct 1992. +     */ + +#define RAND_IA		16807 +#define RAND_IM		2147483647 +#define RAND_IQ		127773 +#define RAND_IR		2836 +#define RAND_MASK	123459876 + +    tmp = iPtr->randSeed/RAND_IQ; +    iPtr->randSeed = RAND_IA*(iPtr->randSeed - tmp*RAND_IQ) - RAND_IR*tmp; +    if (iPtr->randSeed < 0) { +	iPtr->randSeed += RAND_IM; +    } + +    /* +     * Since the recurrence keeps seed values in the range [1, RAND_IM - 1], +     * dividing by RAND_IM yields a double in the range (0, 1). +     */ + +    dResult = iPtr->randSeed * (1.0/RAND_IM); + +    /* +     * Push a Tcl object with the result. +     */ + +    TclNewDoubleObj(oResult, dResult); +    Tcl_SetObjResult(interp, oResult); +    return TCL_OK; +} + +static int +ExprRoundFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Parameter vector. */ +{ +    double d; +    ClientData ptr; +    int type; + +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } + +    if (TclGetNumberFromObj(interp, objv[1], &ptr, &type) != TCL_OK) { +	return TCL_ERROR; +    } + +    if (type == TCL_NUMBER_DOUBLE) { +	double fractPart, intPart; +	long max = LONG_MAX, min = LONG_MIN; + +	fractPart = modf(*((const double *) ptr), &intPart); +	if (fractPart <= -0.5) { +	    min++; +	} else if (fractPart >= 0.5) { +	    max--; +	} +	if ((intPart >= (double)max) || (intPart <= (double)min)) { +	    mp_int big; + +	    if (Tcl_InitBignumFromDouble(interp, intPart, &big) != TCL_OK) { +		/* Infinity */ +		return TCL_ERROR;  	    } +	    if (fractPart <= -0.5) { +		mp_sub_d(&big, 1, &big); +	    } else if (fractPart >= 0.5) { +		mp_add_d(&big, 1, &big); +	    } +	    Tcl_SetObjResult(interp, Tcl_NewBignumObj(&big)); +	    return TCL_OK; +	} else { +	    long result = (long)intPart; + +	    if (fractPart <= -0.5) { +		result--; +	    } else if (fractPart >= 0.5) { +		result++; +	    } +	    Tcl_SetObjResult(interp, Tcl_NewLongObj(result)); +	    return TCL_OK;  	}      } +    if (type != TCL_NUMBER_NAN) { +	/* +	 * All integers are already rounded +	 */ + +	Tcl_SetObjResult(interp, objv[1]); +	return TCL_OK; +    } + +    /* +     * Get the error message for NaN. +     */ + +    Tcl_GetDoubleFromObj(interp, objv[1], &d); +    return TCL_ERROR; +} + +static int +ExprSrandFunc( +    ClientData clientData,	/* Ignored. */ +    Tcl_Interp *interp,		/* The interpreter in which to execute the +				 * function. */ +    int objc,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Parameter vector. */ +{ +    Interp *iPtr = (Interp *) interp; +    long i = 0;			/* Initialized to avoid compiler warning. */ +      /* -     * If the trace forbids bytecode compilation, change the interpreter's -     * state.  If bytecode compilation is now permitted, flag the fact and -     * advance the compilation epoch so that procs will be recompiled to -     * take advantage of it. +     * Convert argument and use it to reset the seed.       */ -    if (!(tracePtr->flags & TCL_ALLOW_INLINE_COMPILATION)) { -	iPtr->tracesForbiddingInline--; -	if (iPtr->tracesForbiddingInline == 0) { -	    iPtr->flags &= ~DONT_COMPILE_CMDS_INLINE; -	    iPtr->compileEpoch++; +    if (objc != 2) { +	MathFuncWrongNumArgs(interp, 2, objc, objv); +	return TCL_ERROR; +    } + +    if (TclGetLongFromObj(NULL, objv[1], &i) != TCL_OK) { +	Tcl_Obj *objPtr; +	mp_int big; + +	if (Tcl_GetBignumFromObj(interp, objv[1], &big) != TCL_OK) { +	    /* TODO: more ::errorInfo here? or in caller? */ +	    return TCL_ERROR;  	} + +	mp_mod_2d(&big, (int) CHAR_BIT * sizeof(long), &big); +	objPtr = Tcl_NewBignumObj(&big); +	Tcl_IncrRefCount(objPtr); +	TclGetLongFromObj(NULL, objPtr, &i); +	Tcl_DecrRefCount(objPtr);      }      /* -     * Execute any delete callback. +     * Reset the seed. Make sure 1 <= randSeed <= 2^31 - 2. See comments in +     * ExprRandFunc for more details.       */ -    if (tracePtr->delProc != NULL) { -	(tracePtr->delProc)(tracePtr->clientData); +    iPtr->flags |= RAND_SEED_INITIALIZED; +    iPtr->randSeed = i; +    iPtr->randSeed &= (unsigned long) 0x7fffffff; +    if ((iPtr->randSeed == 0) || (iPtr->randSeed == 0x7fffffff)) { +	iPtr->randSeed ^= 123459876;      } -    /* Delete the trace object */ +    /* +     * To avoid duplicating the random number generation code we simply clean +     * up our state and call the real random number function. That function +     * will always succeed. +     */ -    Tcl_EventuallyFree((char*)tracePtr, TCL_DYNAMIC); +    return ExprRandFunc(clientData, interp, 1, objv);  }  /*   *----------------------------------------------------------------------   * - * Tcl_AddErrorInfo -- + * MathFuncWrongNumArgs --   * - *	Add information to the "errorInfo" variable that describes the - *	current error. + *	Generate an error message when a math function presents the wrong + *	number of arguments.   *   * Results:   *	None.   *   * Side effects: - *	The contents of message are added to the "errorInfo" variable. - *	If Tcl_Eval has been called since the current value of errorInfo - *	was set, errorInfo is cleared before adding the new message. - *	If we are just starting to log an error, errorInfo is initialized - *	from the error message in the interpreter's result. + *	An error message is stored in the interpreter result.   *   *----------------------------------------------------------------------   */ -void -Tcl_AddErrorInfo(interp, message) -    Tcl_Interp *interp;		/* Interpreter to which error information -				 * pertains. */ -    CONST char *message;	/* Message to record. */ +static void +MathFuncWrongNumArgs( +    Tcl_Interp *interp,		/* Tcl interpreter */ +    int expected,		/* Formal parameter count. */ +    int found,			/* Actual parameter count. */ +    Tcl_Obj *const *objv)	/* Actual parameter vector. */  { -    Tcl_AddObjErrorInfo(interp, message, -1); +    const char *name = Tcl_GetString(objv[0]); +    const char *tail = name + strlen(name); + +    while (tail > name+1) { +	tail--; +	if (*tail == ':' && tail[-1] == ':') { +	    name = tail+1; +	    break; +	} +    } +    Tcl_SetObjResult(interp, Tcl_ObjPrintf( +	    "too %s arguments for math function \"%s\"", +	    (found < expected ? "few" : "many"), name)); +    Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", NULL);  } +#ifdef USE_DTRACE  /*   *----------------------------------------------------------------------   * - * Tcl_AddObjErrorInfo -- + * DTraceObjCmd --   * - *	Add information to the "errorInfo" variable that describes the - *	current error. This routine differs from Tcl_AddErrorInfo by - *	taking a byte pointer and length. + *	This function is invoked to process the "::tcl::dtrace" Tcl command.   *   * Results: - *	None. + *	A standard Tcl object result.   *   * Side effects: - *	"length" bytes from "message" are added to the "errorInfo" variable. - *	If "length" is negative, use bytes up to the first NULL byte. - *	If Tcl_EvalObj has been called since the current value of errorInfo - *	was set, errorInfo is cleared before adding the new message. - *	If we are just starting to log an error, errorInfo is initialized - *	from the error message in the interpreter's result. + *	The 'tcl-probe' DTrace probe is triggered (if it is enabled).   *   *----------------------------------------------------------------------   */ -void -Tcl_AddObjErrorInfo(interp, message, length) -    Tcl_Interp *interp;		/* Interpreter to which error information -				 * pertains. */ -    CONST char *message;	/* Points to the first byte of an array of -				 * bytes of the message. */ -    int length;			/* The number of bytes in the message. -				 * If < 0, then append all bytes up to a -				 * NULL byte. */ +static int +DTraceObjCmd( +    ClientData dummy,		/* Not used. */ +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */  { -    register Interp *iPtr = (Interp *) interp; -    Tcl_Obj *objPtr; -     -    /* -     * If we are just starting to log an error, errorInfo is initialized -     * from the error message in the interpreter's result. -     */ - -    if (!(iPtr->flags & ERR_IN_PROGRESS)) { /* just starting to log error */ -	iPtr->flags |= ERR_IN_PROGRESS; - -	if (iPtr->result[0] == 0) { -	    Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL,  -	            iPtr->objResultPtr, TCL_GLOBAL_ONLY); -	} else {		/* use the string result */ -	    objPtr = Tcl_NewStringObj(interp->result, -1); -	    Tcl_IncrRefCount(objPtr); -	    Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL,  -	            objPtr, TCL_GLOBAL_ONLY); -	    Tcl_DecrRefCount(objPtr); -	} - -	/* -	 * If the errorCode variable wasn't set by the code that generated -	 * the error, set it to "NONE". -	 */ +    if (TCL_DTRACE_TCL_PROBE_ENABLED()) { +	char *a[10]; +	int i = 0; -	if (!(iPtr->flags & ERROR_CODE_SET)) { -	    objPtr = Tcl_NewStringObj("NONE", -1); -	    Tcl_IncrRefCount(objPtr); -	    Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorCode, NULL,  -	            objPtr, TCL_GLOBAL_ONLY); -	    Tcl_DecrRefCount(objPtr); +	while (i++ < 10) { +	    a[i-1] = i < objc ? TclGetString(objv[i]) : NULL;  	} +	TCL_DTRACE_TCL_PROBE(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], +		a[8], a[9]);      } - -    /* -     * Now append "message" to the end of errorInfo. -     */ - -    if (length != 0) { -	objPtr = Tcl_NewStringObj(message, length); -	Tcl_IncrRefCount(objPtr); -	Tcl_ObjSetVar2(interp, iPtr->execEnvPtr->errorInfo, NULL,  -	        objPtr, (TCL_GLOBAL_ONLY | TCL_APPEND_VALUE)); -	Tcl_DecrRefCount(objPtr); /* free msg object appended above */ -    } +    return TCL_OK;  }  /* - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   * - * Tcl_VarEvalVA -- + * TclDTraceInfo --   * - *	Given a variable number of string arguments, concatenate them - *	all together and execute the result as a Tcl command. + *	Extract information from a TIP280 dict for use by DTrace probes.   *   * Results: - *	A standard Tcl return result.  An error message or other result may - *	be left in the interp's result. + *	None.   *   * Side effects: - *	Depends on what was done by the command. + *	None.   * - *--------------------------------------------------------------------------- + *----------------------------------------------------------------------   */ -int -Tcl_VarEvalVA (interp, argList) -    Tcl_Interp *interp;		/* Interpreter in which to evaluate command. */ -    va_list argList;		/* Variable argument list. */ +void +TclDTraceInfo( +    Tcl_Obj *info, +    const char **args, +    int *argsi)  { -    Tcl_DString buf; -    char *string; -    int result; - -    /* -     * Copy the strings one after the other into a single larger -     * string.  Use stack-allocated space for small commands, but if -     * the command gets too large than call ckalloc to create the -     * space. -     */ - -    Tcl_DStringInit(&buf); -    while (1) { -	string = va_arg(argList, char *); -	if (string == NULL) { -	    break; +    static Tcl_Obj *keys[10] = { NULL }; +    Tcl_Obj **k = keys, *val; +    int i = 0; + +    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++) { +	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]); +	} else { +	    argsi[i] = 0;  	} -	Tcl_DStringAppend(&buf, string, -1);      } - -    result = Tcl_Eval(interp, Tcl_DStringValue(&buf)); -    Tcl_DStringFree(&buf); -    return result;  }  /*   *----------------------------------------------------------------------   * - * Tcl_VarEval -- + * DTraceCmdReturn --   * - *	Given a variable number of string arguments, concatenate them - *	all together and execute the result as a Tcl command. + *	NR callback for DTrace command return probes.   *   * Results: - *	A standard Tcl return result.  An error message or other - *	result may be left in interp->result. + *	None.   *   * Side effects: - *	Depends on what was done by the command. + *	None.   *   *----------------------------------------------------------------------   */ -	/* VARARGS2 */ /* ARGSUSED */ -int -Tcl_VarEval TCL_VARARGS_DEF(Tcl_Interp *,arg1) + +static int +DTraceCmdReturn( +    ClientData data[], +    Tcl_Interp *interp, +    int result)  { -    Tcl_Interp *interp; -    va_list argList; -    int result; +    char *cmdName = TclGetString((Tcl_Obj *) data[0]); -    interp = TCL_VARARGS_START(Tcl_Interp *,arg1,argList); -    result = Tcl_VarEvalVA(interp, argList); -    va_end(argList); +    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_GlobalEval -- + * Tcl_NRCallObjProc --   * - *	Evaluate a command at global level in an interpreter. + *	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: - *	A standard Tcl result is returned, and the interp's result is - *	modified accordingly. + *	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: - *	The command string is executed in interp, and the execution - *	is carried out in the variable context of global level (no - *	procedures active), just as if an "uplevel #0" command were - *	being executed. + *	Depends on the objProc.   * - --------------------------------------------------------------------------- + *----------------------------------------------------------------------   */  int -Tcl_GlobalEval(interp, command) -    Tcl_Interp *interp;		/* Interpreter in which to evaluate command. */ -    CONST char *command;	/* Command to evaluate. */ +Tcl_NRCallObjProc( +    Tcl_Interp *interp, +    Tcl_ObjCmdProc *objProc, +    ClientData clientData, +    int objc, +    Tcl_Obj *const objv[])  { -    register Interp *iPtr = (Interp *) interp; -    int result; -    CallFrame *savedVarFramePtr; +    NRE_callback *rootPtr = TOP_CB(interp); -    savedVarFramePtr = iPtr->varFramePtr; -    iPtr->varFramePtr = NULL; -    result = Tcl_Eval(interp, command); -    iPtr->varFramePtr = savedVarFramePtr; -    return result; +    TclNRAddCallback(interp, Dispatch, objProc, clientData, +	    INT2PTR(objc), objv); +    return TclNRRunCallbacks(interp, TCL_OK, rootPtr);  }  /*   *----------------------------------------------------------------------   * - * Tcl_SetRecursionLimit -- + * Tcl_NRCreateCommand --   * - *	Set the maximum number of recursive calls that may be active - *	for an interpreter at once. + *	Define a new NRE-enabled object-based command in a command table.   *   * Results: - *	The return value is the old limit on nesting for interp. + *	The return value is a token for the command, which can be used in + *	future calls to Tcl_GetCommandName.   *   * Side effects: - *	None. + *	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 */ +    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. */ +{ +    Command *cmdPtr = (Command *) +	    Tcl_CreateObjCommand(interp,cmdName,proc,clientData,deleteProc); + +    cmdPtr->nreProc = nreProc; +    return (Tcl_Command) cmdPtr; +} + +/**************************************************************************** + * Stuff for the public api + ****************************************************************************/ +  int -Tcl_SetRecursionLimit(interp, depth) -    Tcl_Interp *interp;			/* Interpreter whose nesting limit -					 * is to be set. */ -    int depth;				/* New value for maximimum depth. */ +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. */ +    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, 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, +    int objc, +    Tcl_Obj *const objv[], +    int flags) +{ +    return TclNREvalObjv(interp, objc, objv, flags|TCL_EVAL_NOERR, +	    (Command *) cmd); +} + +/***************************************************************************** + * Stuff for tailcalls + ***************************************************************************** + * + * Just to show that IT CAN BE DONE! The precise semantics are not simple, + * require more thought. Possibly need a new Tcl return code to do it right? + * Questions include: + *   (1) How is the objc/objv tailcall to be run? My current thinking is that + *	 it should essentially be + *	     [tailcall a b c] <=> [uplevel 1 [list a b c]] + *	 with two caveats + *	     (a) the current frame is dropped first, after running all pending + *		 cleanup tasks and saving its namespace + *	     (b) 'a' is looked up in the returning frame's namespace, but the + *		 command is run in the context to which we are returning + *	 Current implementation does this if [tailcall] is called from within + *	 a proc, errors otherwise. + *   (2) Should a tailcall bypass [catch] in the returning frame? Current + *	 implementation does not (or does it? Changed, test!) - it causes an + *	 error. + * + * FIXME NRE! + */ + +void +TclMarkTailcall( +    Tcl_Interp *interp)  {      Interp *iPtr = (Interp *) interp; -    int old; -    old = iPtr->maxNestingDepth; -    if (depth > 0) { -	iPtr->maxNestingDepth = depth; +    if (iPtr->deferredCallbacks == NULL) { +	TclNRAddCallback(interp, NRCommand, NULL, NULL, +                NULL, NULL); +        iPtr->deferredCallbacks = TOP_CB(interp);      } -    return old; +} + +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++; +} + +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; +} + +int +TclNRTailcallObjCmd( +    ClientData clientData, +    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)) {	/* or is upleveled */ +        Tcl_SetObjResult(interp, Tcl_NewStringObj( +                "tailcall can only be called from a proc or lambda", -1)); +        Tcl_SetErrorCode(interp, "TCL", "TAILCALL", "ILLEGAL", 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. Being lazy: exploit the TclNRAddCallBack macro to +     * build the callback. +     */ + +    if (objc > 1) { +        Tcl_Obj *listPtr, *nsObjPtr; +        Tcl_Namespace *nsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr; +        Tcl_Namespace *ns1Ptr; + +        /* The tailcall data is in a Tcl list: the first element is the +         * namespace, the rest the command to be tailcalled. */ +         +        listPtr = Tcl_NewListObj(objc, objv); + +        nsObjPtr = Tcl_NewStringObj(nsPtr->fullName, -1); +        if ((TCL_OK != TclGetNamespaceFromObj(interp, nsObjPtr, &ns1Ptr)) +                || (nsPtr != ns1Ptr)) { +            Tcl_Panic("Tailcall failed to find the proper namespace"); +        } + 	TclListObjSetElement(interp, listPtr, 0, nsObjPtr); +         +        iPtr->varFramePtr->tailcallPtr = listPtr; +    } +    return TCL_RETURN; +} + +int +TclNRTailcallEval( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Interp *iPtr = (Interp *) interp; +    Tcl_Obj *listPtr = data[0], *nsObjPtr; +    Tcl_Namespace *nsPtr; +    int objc; +    Tcl_Obj **objv; + +    Tcl_ListObjGetElements(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. +         */ + +        TailcallCleanup(data, interp, result); +        return result; +    } + +    /* +     * Perform the tailcall +     */ + +    TclMarkTailcall(interp); +    TclNRAddCallback(interp, TailcallCleanup, listPtr, NULL, NULL,NULL); +    iPtr->lookupNsPtr = (Namespace *) nsPtr; +    return TclNREvalObjv(interp, objc-1, objv+1, 0, NULL); +} + +static int +TailcallCleanup( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    Tcl_DecrRefCount((Tcl_Obj *) data[0]); +    return result; +} + + +void +Tcl_NRAddCallback( +    Tcl_Interp *interp, +    Tcl_NRPostProc *postProcPtr, +    ClientData data0, +    ClientData data1, +    ClientData data2, +    ClientData data3) +{ +    if (!(postProcPtr)) { +	Tcl_Panic("Adding a callback without an objProc?!"); +    } +    TclNRAddCallback(interp, postProcPtr, data0, data1, data2, data3);  }  /*   *----------------------------------------------------------------------   * - * Tcl_AllowExceptions -- + * TclNRCoroutineObjCmd -- (and friends)   * - *	Sets a flag in an interpreter so that exceptions can occur - *	in the next call to Tcl_Eval without them being turned into - *	errors. + *	This object-based function is invoked to process the "coroutine" Tcl + *	command. It is heavily based on "apply".   *   * Results: - *	None. + *	A standard Tcl object result value.   *   * Side effects: - *	The TCL_ALLOW_EXCEPTIONS flag gets set in the interpreter's - *	evalFlags structure.  See the reference documentation for - *	more details. + *	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.   *----------------------------------------------------------------------   */ -void -Tcl_AllowExceptions(interp) -    Tcl_Interp *interp;		/* Interpreter in which to set flag. */ +#define iPtr ((Interp *) interp) + +int +TclNRYieldObjCmd( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[])  { -    Interp *iPtr = (Interp *) interp; +    CoroutineData *corPtr = iPtr->execEnvPtr->corPtr; -    iPtr->evalFlags |= TCL_ALLOW_EXCEPTIONS; +    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", -1)); +	Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ILLEGAL_YIELD", 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( +    ClientData clientData, +    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", -1)); +	Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ILLEGAL_YIELD", NULL); +	return TCL_ERROR; +    } + +    if (((Namespace *) nsPtr)->flags & NS_DYING) { +        Tcl_SetObjResult(interp, Tcl_NewStringObj( +		"yieldto called in deleted namespace", -1)); +        Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "YIELDTO_IN_DELETED", +		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, -1); +    TclListObjSetElement(interp, listPtr, 0, nsObjPtr); + +    /* +     * Add the callback in the caller's env, then instruct TEBC to yield. +     */ + +    iPtr->execEnvPtr = corPtr->callerEEPtr; +    TclSetTailcall(interp, listPtr); +    iPtr->execEnvPtr = corPtr->eePtr; + +    return TclNRYieldObjCmd(INT2PTR(CORO_ACTIVATE_YIELDM), interp, 1, objv); +} + +static int +RewindCoroutineCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    return Tcl_RestoreInterpState(interp, 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( +    ClientData clientData) +{ +    CoroutineData *corPtr = 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( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    CoroutineData *corPtr = 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_IS_DELETED) { +	/* +	 * 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( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    CoroutineData *corPtr = 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; +}  /*   *----------------------------------------------------------------------   * - * Tcl_GetVersion + * TclNRCoroutineActivateCallback --   * - *	Get the Tcl major, minor, and patchlevel version numbers and - *      the release type.  A patch is a release type TCL_FINAL_RELEASE - *      with a patchLevel > 0. + *      This is the workhorse for coroutines: it implements both yield and + *      resume.   * - * Results: - *	None. + *      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.   * - * Side effects: - *	None. + *---------------------------------------------------------------------- + */ + +int +TclNRCoroutineActivateCallback( +    ClientData data[], +    Tcl_Interp *interp, +    int result) +{ +    CoroutineData *corPtr = data[0]; +    int type = PTR2INT(data[1]); +    int numLevels, unused; +    int *stackLevel = &unused; + +    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; +        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) { +            Tcl_SetObjResult(interp, Tcl_NewStringObj( +                    "cannot yield: C stack busy", -1)); +            Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "CANT_YIELD", +                    NULL); +            return TCL_ERROR; +        } + +        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->stackLevel = NULL; + +        numLevels = iPtr->numLevels; +        iPtr->numLevels = corPtr->auxNumLevels; +        corPtr->auxNumLevels = numLevels - corPtr->auxNumLevels; + +        iPtr->execEnvPtr = corPtr->callerEEPtr; +    } + +    return TCL_OK; +} + +/* + *---------------------------------------------------------------------- + * + * NRCoroInjectObjCmd -- + * + *      Implementation of [::tcl::unsupported::inject] command.   *   *----------------------------------------------------------------------   */ -void -Tcl_GetVersion(majorV, minorV, patchLevelV, type) -    int *majorV; -    int *minorV; -    int *patchLevelV; -    int *type; +static int +NRCoroInjectObjCmd( +    ClientData clientData, +    Tcl_Interp *interp, +    int objc, +    Tcl_Obj *const objv[])  { -    if (majorV != NULL) { -        *majorV = TCL_MAJOR_VERSION; +    Command *cmdPtr; +    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;      } -    if (minorV != NULL) { -        *minorV = TCL_MINOR_VERSION; + +    cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[1]); +    if ((!cmdPtr) || (cmdPtr->nreProc != TclNRInterpCoroutine)) { +        Tcl_SetObjResult(interp, Tcl_NewStringObj( +                "can only inject a command into a coroutine", -1)); +        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "COROUTINE", +                TclGetString(objv[1]), NULL); +        return TCL_ERROR;      } -    if (patchLevelV != NULL) { -        *patchLevelV = TCL_RELEASE_SERIAL; + +    corPtr = cmdPtr->objClientData; +    if (!COR_IS_SUSPENDED(corPtr)) { +        Tcl_SetObjResult(interp, Tcl_NewStringObj( +                "can only inject a command into a suspended coroutine", -1)); +        Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "ACTIVE", NULL); +        return TCL_ERROR;      } -    if (type != NULL) { -        *type = TCL_RELEASE_LEVEL; + +    /* +     * 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; +    TclNREvalObjEx(interp, Tcl_NewListObj(objc-2, objv+2), 0, NULL, INT_MIN); +    iPtr->execEnvPtr = savedEEPtr; + +    return TCL_OK; +} + +int +TclNRInterpCoroutine( +    ClientData clientData, +    Tcl_Interp *interp,		/* Current interpreter. */ +    int objc,			/* Number of arguments. */ +    Tcl_Obj *const objv[])	/* Argument objects. */ +{ +    CoroutineData *corPtr = clientData; + +    if (!COR_IS_SUSPENDED(corPtr)) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "coroutine \"%s\" is already running", +                Tcl_GetString(objv[0]))); +	Tcl_SetErrorCode(interp, "TCL", "COROUTINE", "BUSY", 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 != objc-1) { +            Tcl_SetObjResult(interp, +                    Tcl_NewStringObj("wrong coro nargs; how did we get here? " +                    "not implemented!", -1)); +            Tcl_SetErrorCode(interp, "TCL", "WRONGARGS", 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;  } -#ifdef USE_DTRACE  /*   *----------------------------------------------------------------------   * - * DTraceObjCmd -- - * - *	This function is invoked to process the "::tcl::dtrace" Tcl command. - * - * Results: - *	A standard Tcl object result. + * TclNRCoroutineObjCmd --   * - * Side effects: - *	The 'tcl-probe' DTrace probe is triggered (if it is enabled). + *      Implementation of [coroutine] command; see documentation for + *      description of what this does.   *   *----------------------------------------------------------------------   */ -static int -DTraceObjCmd( +int +TclNRCoroutineObjCmd(      ClientData dummy,		/* Not used. */      Tcl_Interp *interp,		/* Current interpreter. */      int objc,			/* Number of arguments. */ -    Tcl_Obj *CONST objv[])	/* Argument objects. */ +    Tcl_Obj *const objv[])	/* Argument objects. */  { -    if (TCL_DTRACE_TCL_PROBE_ENABLED()) { -	char *a[10]; -	int i = 0; +    Command *cmdPtr; +    CoroutineData *corPtr; +    const char *fullName, *procName; +    Namespace *nsPtr, *altNsPtr, *cxtNsPtr; +    Tcl_DString ds; +    Namespace *lookupNsPtr = iPtr->varFramePtr->nsPtr; + +    if (objc < 3) { +	Tcl_WrongNumArgs(interp, 1, objv, "name cmd ?arg ...?"); +	return TCL_ERROR; +    } -	while (i++ < 10) { -	    a[i-1] = i < objc ? TclGetString(objv[i]) : NULL; +    /* +     * FIXME: this is copy/pasted from Tcl_ProcObjCommand. Should have +     * something in tclUtil.c to find the FQ name. +     */ + +    fullName = TclGetString(objv[1]); +    TclGetNamespaceForQualName(interp, fullName, NULL, 0, +	    &nsPtr, &altNsPtr, &cxtNsPtr, &procName); + +    if (nsPtr == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can't create procedure \"%s\": unknown namespace", +                fullName)); +        Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "NAMESPACE", NULL); +	return TCL_ERROR; +    } +    if (procName == NULL) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can't create procedure \"%s\": bad procedure name", +                fullName)); +        Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", fullName, NULL); +	return TCL_ERROR; +    } +    if ((nsPtr != iPtr->globalNsPtr) +	    && (procName != NULL) && (procName[0] == ':')) { +	Tcl_SetObjResult(interp, Tcl_ObjPrintf( +                "can't create procedure \"%s\" in non-global namespace with" +                " name starting with \":\"", procName)); +        Tcl_SetErrorCode(interp, "TCL", "VALUE", "COMMAND", procName, NULL); +	return TCL_ERROR; +    } + +    /* +     * We ARE creating the coroutine command: allocate the corresponding +     * struct and create the corresponding command. +     */ + +    corPtr = ckalloc(sizeof(CoroutineData)); + +    Tcl_DStringInit(&ds); +    if (nsPtr != iPtr->globalNsPtr) { +	Tcl_DStringAppend(&ds, nsPtr->fullName, -1); +	TclDStringAppendLiteral(&ds, "::"); +    } +    Tcl_DStringAppend(&ds, procName, -1); + +    cmdPtr = (Command *) Tcl_NRCreateCommand(interp, Tcl_DStringValue(&ds), +	    /*objProc*/ NULL, TclNRInterpCoroutine, corPtr, DeleteCoroutine); +    Tcl_DStringFree(&ds); + +    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 = 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));  	} -	TCL_DTRACE_TCL_PROBE(a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], -		a[8], a[9]);      } + +    /* +     * 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; + +    /* +     * 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); + +    /* insure 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( +    ClientData dummy, +    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; +    } -TCL_DTRACE_DEBUG_LOG(); +    if (corPtr && !(corPtr->cmdPtr->flags & CMD_IS_DELETED)) { +	Tcl_Obj *namePtr; -#endif /* USE_DTRACE */ +	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:   */ - | 
