tclCompCmds.c

Go to the documentation of this file.
00001 /*
00002  * tclCompCmds.c --
00003  *
00004  *      This file contains compilation procedures that compile various Tcl
00005  *      commands into a sequence of instructions ("bytecodes").
00006  *
00007  * Copyright (c) 1997-1998 Sun Microsystems, Inc.
00008  * Copyright (c) 2001 by Kevin B. Kenny.  All rights reserved.
00009  * Copyright (c) 2002 ActiveState Corporation.
00010  * Copyright (c) 2004-2006 by Donal K. Fellows.
00011  *
00012  * See the file "license.terms" for information on usage and redistribution of
00013  * this file, and for a DISCLAIMER OF ALL WARRANTIES.
00014  *
00015  * RCS: @(#) $Id: tclCompCmds.c,v 1.140 2008/01/23 19:41:27 dgp Exp $
00016  */
00017 
00018 #include "tclInt.h"
00019 #include "tclCompile.h"
00020 
00021 /*
00022  * Macro that encapsulates an efficiency trick that avoids a function call for
00023  * the simplest of compiles. The ANSI C "prototype" for this macro is:
00024  *
00025  * static void          CompileWord(CompileEnv *envPtr, Tcl_Token *tokenPtr,
00026  *                          Tcl_Interp *interp, int word);
00027  */
00028 
00029 #define CompileWord(envPtr, tokenPtr, interp, word) \
00030     if ((tokenPtr)->type == TCL_TOKEN_SIMPLE_WORD) { \
00031         TclEmitPush(TclRegisterNewLiteral((envPtr), (tokenPtr)[1].start, \
00032                 (tokenPtr)[1].size), (envPtr)); \
00033     } else { \
00034         envPtr->line = mapPtr->loc[eclIndex].line[word]; \
00035         TclCompileTokens((interp), (tokenPtr)+1, (tokenPtr)->numComponents, \
00036                 (envPtr)); \
00037     }
00038 
00039 /*
00040  * TIP #280: Remember the per-word line information of the current command. An
00041  * index is used instead of a pointer as recursive compilation may reallocate,
00042  * i.e. move, the array. This is also the reason to save the nuloc now, it may
00043  * change during the course of the function.
00044  *
00045  * Macro to encapsulate the variable definition and setup.
00046  */
00047 
00048 #define DefineLineInformation \
00049     ExtCmdLoc *mapPtr = envPtr->extCmdMapPtr; \
00050     int eclIndex = mapPtr->nuloc - 1
00051 
00052 /*
00053  * Convenience macro for use when compiling bodies of commands. The ANSI C
00054  * "prototype" for this macro is:
00055  *
00056  * static void          CompileBody(CompileEnv *envPtr, Tcl_Token *tokenPtr,
00057  *                          Tcl_Interp *interp);
00058  */
00059 
00060 #define CompileBody(envPtr, tokenPtr, interp) \
00061     TclCompileCmdWord((interp), (tokenPtr)+1, (tokenPtr)->numComponents, \
00062             (envPtr))
00063 
00064 /*
00065  * Convenience macro for use when compiling tokens to be pushed. The ANSI C
00066  * "prototype" for this macro is:
00067  *
00068  * static void          CompileTokens(CompileEnv *envPtr, Tcl_Token *tokenPtr,
00069  *                          Tcl_Interp *interp);
00070  */
00071 
00072 #define CompileTokens(envPtr, tokenPtr, interp) \
00073     TclCompileTokens((interp), (tokenPtr)+1, (tokenPtr)->numComponents, \
00074             (envPtr));
00075 /*
00076  * Convenience macro for use when pushing literals. The ANSI C "prototype" for
00077  * this macro is:
00078  *
00079  * static void          PushLiteral(CompileEnv *envPtr,
00080  *                          const char *string, int length);
00081  */
00082 
00083 #define PushLiteral(envPtr, string, length) \
00084     TclEmitPush(TclRegisterNewLiteral((envPtr), (string), (length)), (envPtr))
00085 
00086 /*
00087  * Macro to advance to the next token; it is more mnemonic than the address
00088  * arithmetic that it replaces. The ANSI C "prototype" for this macro is:
00089  *
00090  * static Tcl_Token *   TokenAfter(Tcl_Token *tokenPtr);
00091  */
00092 
00093 #define TokenAfter(tokenPtr) \
00094     ((tokenPtr) + ((tokenPtr)->numComponents + 1))
00095 
00096 /*
00097  * Macro to get the offset to the next instruction to be issued. The ANSI C
00098  * "prototype" for this macro is:
00099  *
00100  * static int   CurrentOffset(CompileEnv *envPtr);
00101  */
00102 
00103 #define CurrentOffset(envPtr) \
00104     ((envPtr)->codeNext - (envPtr)->codeStart)
00105 
00106 /*
00107  * Note: the exceptDepth is a bit of a misnomer: TEBC only needs the
00108  * maximal depth of nested CATCH ranges in order to alloc runtime
00109  * memory. These macros should compute precisely that? OTOH, the nesting depth
00110  * of LOOP ranges is an interesting datum for debugging purposes, and that is
00111  * what we compute now.
00112  *
00113  * static int   DeclareExceptionRange(CompileEnv *envPtr, int type);
00114  * static int   ExceptionRangeStarts(CompileEnv *envPtr, int index);
00115  * static void  ExceptionRangeEnds(CompileEnv *envPtr, int index);
00116  * static void  ExceptionRangeTarget(CompileEnv *envPtr, int index, LABEL);
00117  */
00118 
00119 #define DeclareExceptionRange(envPtr, type) \
00120     (TclCreateExceptRange((type), (envPtr)))
00121 #define ExceptionRangeStarts(envPtr, index) \
00122     (((envPtr)->exceptDepth++), \
00123     ((envPtr)->maxExceptDepth = \
00124             TclMax((envPtr)->exceptDepth, (envPtr)->maxExceptDepth)), \
00125     ((envPtr)->exceptArrayPtr[(index)].codeOffset = CurrentOffset(envPtr)))
00126 #define ExceptionRangeEnds(envPtr, index) \
00127     (((envPtr)->exceptDepth--), \
00128     ((envPtr)->exceptArrayPtr[(index)].numCodeBytes = \
00129         CurrentOffset(envPtr) - (envPtr)->exceptArrayPtr[(index)].codeOffset))
00130 #define ExceptionRangeTarget(envPtr, index, targetType) \
00131     ((envPtr)->exceptArrayPtr[(index)].targetType = CurrentOffset(envPtr))
00132 
00133 /*
00134  * Prototypes for procedures defined later in this file:
00135  */
00136 
00137 static ClientData       DupDictUpdateInfo(ClientData clientData);
00138 static void             FreeDictUpdateInfo(ClientData clientData);
00139 static void             PrintDictUpdateInfo(ClientData clientData,
00140                             Tcl_Obj *appendObj, ByteCode *codePtr,
00141                             unsigned int pcOffset);
00142 static ClientData       DupForeachInfo(ClientData clientData);
00143 static void             FreeForeachInfo(ClientData clientData);
00144 static void             PrintForeachInfo(ClientData clientData,
00145                             Tcl_Obj *appendObj, ByteCode *codePtr,
00146                             unsigned int pcOffset);
00147 static ClientData       DupJumptableInfo(ClientData clientData);
00148 static void             FreeJumptableInfo(ClientData clientData);
00149 static void             PrintJumptableInfo(ClientData clientData,
00150                             Tcl_Obj *appendObj, ByteCode *codePtr,
00151                             unsigned int pcOffset);
00152 static int              PushVarName(Tcl_Interp *interp,
00153                             Tcl_Token *varTokenPtr, CompileEnv *envPtr,
00154                             int flags, int *localIndexPtr,
00155                             int *simpleVarNamePtr, int *isScalarPtr, int line);
00156 static int              CompileAssociativeBinaryOpCmd(Tcl_Interp *interp,
00157                             Tcl_Parse *parsePtr, const char *identity,
00158                             int instruction, CompileEnv *envPtr);
00159 static int              CompileComparisonOpCmd(Tcl_Interp *interp,
00160                             Tcl_Parse *parsePtr, int instruction,
00161                             CompileEnv *envPtr);
00162 static int              CompileStrictlyBinaryOpCmd(Tcl_Interp *interp,
00163                             Tcl_Parse *parsePtr, int instruction,
00164                             CompileEnv *envPtr);
00165 static int              CompileUnaryOpCmd(Tcl_Interp *interp,
00166                             Tcl_Parse *parsePtr, int instruction,
00167                             CompileEnv *envPtr);
00168 static void             CompileReturnInternal(CompileEnv *envPtr,
00169                             unsigned char op, int code, int level,
00170                             Tcl_Obj *returnOpts);
00171 
00172 /*
00173  * Flags bits used by PushVarName.
00174  */
00175 
00176 #define TCL_CREATE_VAR     1    /* Create a compiled local if none is found */
00177 #define TCL_NO_LARGE_INDEX 2    /* Do not return localIndex value > 255 */
00178 
00179 /*
00180  * The structures below define the AuxData types defined in this file.
00181  */
00182 
00183 AuxDataType tclForeachInfoType = {
00184     "ForeachInfo",              /* name */
00185     DupForeachInfo,             /* dupProc */
00186     FreeForeachInfo,            /* freeProc */
00187     PrintForeachInfo            /* printProc */
00188 };
00189 
00190 AuxDataType tclJumptableInfoType = {
00191     "JumptableInfo",            /* name */
00192     DupJumptableInfo,           /* dupProc */
00193     FreeJumptableInfo,          /* freeProc */
00194     PrintJumptableInfo          /* printProc */
00195 };
00196 
00197 AuxDataType tclDictUpdateInfoType = {
00198     "DictUpdateInfo",           /* name */
00199     DupDictUpdateInfo,          /* dupProc */
00200     FreeDictUpdateInfo,         /* freeProc */
00201     PrintDictUpdateInfo         /* printProc */
00202 };
00203 
00204 /*
00205  *----------------------------------------------------------------------
00206  *
00207  * TclCompileAppendCmd --
00208  *
00209  *      Procedure called to compile the "append" command.
00210  *
00211  * Results:
00212  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
00213  *      evaluation to runtime.
00214  *
00215  * Side effects:
00216  *      Instructions are added to envPtr to execute the "append" command at
00217  *      runtime.
00218  *
00219  *----------------------------------------------------------------------
00220  */
00221 
00222 int
00223 TclCompileAppendCmd(
00224     Tcl_Interp *interp,         /* Used for error reporting. */
00225     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00226                                  * created by Tcl_ParseCommand. */
00227     Command *cmdPtr,            /* Points to defintion of command being
00228                                  * compiled. */
00229     CompileEnv *envPtr)         /* Holds resulting instructions. */
00230 {
00231     Tcl_Token *varTokenPtr, *valueTokenPtr;
00232     int simpleVarName, isScalar, localIndex, numWords;
00233     DefineLineInformation;      /* TIP #280 */
00234 
00235     numWords = parsePtr->numWords;
00236     if (numWords == 1) {
00237         return TCL_ERROR;
00238     } else if (numWords == 2) {
00239         /*
00240          * append varName == set varName
00241          */
00242 
00243         return TclCompileSetCmd(interp, parsePtr, cmdPtr, envPtr);
00244     } else if (numWords > 3) {
00245         /*
00246          * APPEND instructions currently only handle one value.
00247          */
00248 
00249         return TCL_ERROR;
00250     }
00251 
00252     /*
00253      * Decide if we can use a frame slot for the var/array name or if we need
00254      * to emit code to compute and push the name at runtime. We use a frame
00255      * slot (entry in the array of local vars) if we are compiling a procedure
00256      * body and if the name is simple text that does not include namespace
00257      * qualifiers.
00258      */
00259 
00260     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
00261 
00262     PushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
00263                 &localIndex, &simpleVarName, &isScalar,
00264                 mapPtr->loc[eclIndex].line[1]);
00265 
00266     /*
00267      * We are doing an assignment, otherwise TclCompileSetCmd was called, so
00268      * push the new value. This will need to be extended to push a value for
00269      * each argument.
00270      */
00271 
00272     if (numWords > 2) {
00273         valueTokenPtr = TokenAfter(varTokenPtr);
00274         CompileWord(envPtr, valueTokenPtr, interp, 2);
00275     }
00276 
00277     /*
00278      * Emit instructions to set/get the variable.
00279      */
00280 
00281     if (simpleVarName) {
00282         if (isScalar) {
00283             if (localIndex < 0) {
00284                 TclEmitOpcode(INST_APPEND_STK, envPtr);
00285             } else if (localIndex <= 255) {
00286                 TclEmitInstInt1(INST_APPEND_SCALAR1, localIndex, envPtr);
00287             } else {
00288                 TclEmitInstInt4(INST_APPEND_SCALAR4, localIndex, envPtr);
00289             }
00290         } else {
00291             if (localIndex < 0) {
00292                 TclEmitOpcode(INST_APPEND_ARRAY_STK, envPtr);
00293             } else if (localIndex <= 255) {
00294                 TclEmitInstInt1(INST_APPEND_ARRAY1, localIndex, envPtr);
00295             } else {
00296                 TclEmitInstInt4(INST_APPEND_ARRAY4, localIndex, envPtr);
00297             }
00298         }
00299     } else {
00300         TclEmitOpcode(INST_APPEND_STK, envPtr);
00301     }
00302 
00303     return TCL_OK;
00304 }
00305 
00306 /*
00307  *----------------------------------------------------------------------
00308  *
00309  * TclCompileBreakCmd --
00310  *
00311  *      Procedure called to compile the "break" command.
00312  *
00313  * Results:
00314  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
00315  *      evaluation to runtime.
00316  *
00317  * Side effects:
00318  *      Instructions are added to envPtr to execute the "break" command at
00319  *      runtime.
00320  *
00321  *----------------------------------------------------------------------
00322  */
00323 
00324 int
00325 TclCompileBreakCmd(
00326     Tcl_Interp *interp,         /* Used for error reporting. */
00327     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00328                                  * created by Tcl_ParseCommand. */
00329     Command *cmdPtr,            /* Points to defintion of command being
00330                                  * compiled. */
00331     CompileEnv *envPtr)         /* Holds resulting instructions. */
00332 {
00333     if (parsePtr->numWords != 1) {
00334         return TCL_ERROR;
00335     }
00336 
00337     /*
00338      * Emit a break instruction.
00339      */
00340 
00341     TclEmitOpcode(INST_BREAK, envPtr);
00342     return TCL_OK;
00343 }
00344 
00345 /*
00346  *----------------------------------------------------------------------
00347  *
00348  * TclCompileCatchCmd --
00349  *
00350  *      Procedure called to compile the "catch" command.
00351  *
00352  * Results:
00353  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
00354  *      evaluation to runtime.
00355  *
00356  * Side effects:
00357  *      Instructions are added to envPtr to execute the "catch" command at
00358  *      runtime.
00359  *
00360  *----------------------------------------------------------------------
00361  */
00362 
00363 int
00364 TclCompileCatchCmd(
00365     Tcl_Interp *interp,         /* Used for error reporting. */
00366     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00367                                  * created by Tcl_ParseCommand. */
00368     Command *cmdPtr,            /* Points to defintion of command being
00369                                  * compiled. */
00370     CompileEnv *envPtr)         /* Holds resulting instructions. */
00371 {
00372     JumpFixup jumpFixup;
00373     Tcl_Token *cmdTokenPtr, *resultNameTokenPtr, *optsNameTokenPtr;
00374     const char *name;
00375     int resultIndex, optsIndex, nameChars, range;
00376     int savedStackDepth = envPtr->currStackDepth;
00377     DefineLineInformation;      /* TIP #280 */
00378 
00379     /*
00380      * If syntax does not match what we expect for [catch], do not compile.
00381      * Let runtime checks determine if syntax has changed.
00382      */
00383 
00384     if ((parsePtr->numWords < 2) || (parsePtr->numWords > 4)) {
00385         return TCL_ERROR;
00386     }
00387 
00388     /*
00389      * If variables were specified and the catch command is at global level
00390      * (not in a procedure), don't compile it inline: the payoff is too small.
00391      */
00392 
00393     if ((parsePtr->numWords >= 3) && (envPtr->procPtr == NULL)) {
00394         return TCL_ERROR;
00395     }
00396 
00397     /*
00398      * Make sure the variable names, if any, have no substitutions and just
00399      * refer to local scalars.
00400      */
00401 
00402     resultIndex = optsIndex = -1;
00403     cmdTokenPtr = TokenAfter(parsePtr->tokenPtr);
00404     if (parsePtr->numWords >= 3) {
00405         resultNameTokenPtr = TokenAfter(cmdTokenPtr);
00406         /* DGP */
00407         if (resultNameTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
00408             return TCL_ERROR;
00409         }
00410 
00411         name = resultNameTokenPtr[1].start;
00412         nameChars = resultNameTokenPtr[1].size;
00413         if (!TclIsLocalScalar(name, nameChars)) {
00414             return TCL_ERROR;
00415         }
00416         resultIndex = TclFindCompiledLocal(resultNameTokenPtr[1].start,
00417                 resultNameTokenPtr[1].size, /*create*/ 1, envPtr->procPtr);
00418 
00419         /* DKF */
00420         if (parsePtr->numWords == 4) {
00421             optsNameTokenPtr = TokenAfter(resultNameTokenPtr);
00422             if (optsNameTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
00423                 return TCL_ERROR;
00424             }
00425             name = optsNameTokenPtr[1].start;
00426             nameChars = optsNameTokenPtr[1].size;
00427             if (!TclIsLocalScalar(name, nameChars)) {
00428                 return TCL_ERROR;
00429             }
00430             optsIndex = TclFindCompiledLocal(optsNameTokenPtr[1].start,
00431                     optsNameTokenPtr[1].size, /*create*/ 1, envPtr->procPtr);
00432         }
00433     }
00434 
00435     /*
00436      * We will compile the catch command. Emit a beginCatch instruction at the
00437      * start of the catch body: the subcommand it controls.
00438      */
00439 
00440     range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
00441     TclEmitInstInt4(INST_BEGIN_CATCH4, range, envPtr);
00442 
00443     /*
00444      * If the body is a simple word, compile the instructions to eval it.
00445      * Otherwise, compile instructions to substitute its text without
00446      * catching, a catch instruction that resets the stack to what it was
00447      * before substituting the body, and then an instruction to eval the body.
00448      * Care has to be taken to register the correct startOffset for the catch
00449      * range so that errors in the substitution are not catched [Bug 219184]
00450      */
00451 
00452     envPtr->line = mapPtr->loc[eclIndex].line[1];
00453     if (cmdTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
00454         ExceptionRangeStarts(envPtr, range);
00455         CompileBody(envPtr, cmdTokenPtr, interp);
00456         ExceptionRangeEnds(envPtr, range);
00457     } else {
00458         CompileTokens(envPtr, cmdTokenPtr, interp);
00459         ExceptionRangeStarts(envPtr, range);
00460         TclEmitOpcode(INST_EVAL_STK, envPtr);
00461         ExceptionRangeEnds(envPtr, range);
00462     }
00463 
00464     /*
00465      * The "no errors" epilogue code: store the body's result into the
00466      * variable (if any), push "0" (TCL_OK) as the catch's "no error" result,
00467      * and jump around the "error case" code. Note that we issue the push of
00468      * the return options first so that if alterations happen to the current
00469      * interpreter state during the writing of the variable, we won't see
00470      * them; this results in a slightly complex instruction issuing flow
00471      * (can't exchange, only duplicate and pop).
00472      */
00473 
00474     if (resultIndex != -1) {
00475         if (optsIndex != -1) {
00476             TclEmitOpcode(INST_PUSH_RETURN_OPTIONS, envPtr);
00477             TclEmitInstInt4(INST_OVER, 1, envPtr);
00478         }
00479         if (resultIndex <= 255) {
00480             TclEmitInstInt1(INST_STORE_SCALAR1, resultIndex, envPtr);
00481         } else {
00482             TclEmitInstInt4(INST_STORE_SCALAR4, resultIndex, envPtr);
00483         }
00484         if (optsIndex != -1) {
00485             TclEmitOpcode(INST_POP, envPtr);
00486             if (optsIndex <= 255) {
00487                 TclEmitInstInt1(INST_STORE_SCALAR1, optsIndex, envPtr);
00488             } else {
00489                 TclEmitInstInt4(INST_STORE_SCALAR4, optsIndex, envPtr);
00490             }
00491             TclEmitOpcode(INST_POP, envPtr);
00492         }
00493     }
00494     TclEmitOpcode(INST_POP, envPtr);
00495     PushLiteral(envPtr, "0", 1);
00496     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);
00497 
00498     /*
00499      * The "error case" code: store the body's result into the variable (if
00500      * any), then push the error result code. The initial PC offset here is
00501      * the catch's error target. Note that if we are saving the return
00502      * options, we do that first so the preservation cannot get affected by
00503      * any intermediate result handling.
00504      */
00505 
00506     envPtr->currStackDepth = savedStackDepth;
00507     ExceptionRangeTarget(envPtr, range, catchOffset);
00508     if (resultIndex != -1) {
00509         if (optsIndex != -1) {
00510             TclEmitOpcode(INST_PUSH_RETURN_OPTIONS, envPtr);
00511         }
00512         TclEmitOpcode(INST_PUSH_RESULT, envPtr);
00513         if (resultIndex <= 255) {
00514             TclEmitInstInt1(INST_STORE_SCALAR1, resultIndex, envPtr);
00515         } else {
00516             TclEmitInstInt4(INST_STORE_SCALAR4, resultIndex, envPtr);
00517         }
00518         TclEmitOpcode(INST_POP, envPtr);
00519         if (optsIndex != -1) {
00520             if (optsIndex <= 255) {
00521                 TclEmitInstInt1(INST_STORE_SCALAR1, optsIndex, envPtr);
00522             } else {
00523                 TclEmitInstInt4(INST_STORE_SCALAR4, optsIndex, envPtr);
00524             }
00525             TclEmitOpcode(INST_POP, envPtr);
00526         }
00527     }
00528     TclEmitOpcode(INST_PUSH_RETURN_CODE, envPtr);
00529 
00530     /*
00531      * Update the target of the jump after the "no errors" code, then emit an
00532      * endCatch instruction at the end of the catch command.
00533      */
00534 
00535     if (TclFixupForwardJumpToHere(envPtr, &jumpFixup, 127)) {
00536         Tcl_Panic("TclCompileCatchCmd: bad jump distance %d",
00537                 CurrentOffset(envPtr) - jumpFixup.codeOffset);
00538     }
00539     TclEmitOpcode(INST_END_CATCH, envPtr);
00540 
00541     envPtr->currStackDepth = savedStackDepth + 1;
00542     return TCL_OK;
00543 }
00544 
00545 /*
00546  *----------------------------------------------------------------------
00547  *
00548  * TclCompileContinueCmd --
00549  *
00550  *      Procedure called to compile the "continue" command.
00551  *
00552  * Results:
00553  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
00554  *      evaluation to runtime.
00555  *
00556  * Side effects:
00557  *      Instructions are added to envPtr to execute the "continue" command at
00558  *      runtime.
00559  *
00560  *----------------------------------------------------------------------
00561  */
00562 
00563 int
00564 TclCompileContinueCmd(
00565     Tcl_Interp *interp,         /* Used for error reporting. */
00566     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00567                                  * created by Tcl_ParseCommand. */
00568     Command *cmdPtr,            /* Points to defintion of command being
00569                                  * compiled. */
00570     CompileEnv *envPtr)         /* Holds resulting instructions. */
00571 {
00572     /*
00573      * There should be no argument after the "continue".
00574      */
00575 
00576     if (parsePtr->numWords != 1) {
00577         return TCL_ERROR;
00578     }
00579 
00580     /*
00581      * Emit a continue instruction.
00582      */
00583 
00584     TclEmitOpcode(INST_CONTINUE, envPtr);
00585     return TCL_OK;
00586 }
00587 
00588 /*
00589  *----------------------------------------------------------------------
00590  *
00591  * TclCompileDict*Cmd --
00592  *
00593  *      Functions called to compile "dict" sucommands.
00594  *
00595  * Results:
00596  *      All return TCL_OK for a successful compile, and TCL_ERROR to defer
00597  *      evaluation to runtime.
00598  *
00599  * Side effects:
00600  *      Instructions are added to envPtr to execute the "dict" subcommand at
00601  *      runtime.
00602  *
00603  * Notes:
00604  *      The following commands are in fairly common use and are possibly worth
00605  *      bytecoding:
00606  *              dict append
00607  *              dict create     [*]
00608  *              dict exists     [*]
00609  *              dict for
00610  *              dict get        [*]
00611  *              dict incr
00612  *              dict keys       [*]
00613  *              dict lappend
00614  *              dict set
00615  *              dict unset
00616  *
00617  *      In practice, those that are pure-value operators (marked with [*]) can
00618  *      probably be left alone (except perhaps [dict get] which is very very
00619  *      common) and [dict update] should be considered instead (really big
00620  *      win!)
00621  *
00622  *----------------------------------------------------------------------
00623  */
00624 
00625 int
00626 TclCompileDictSetCmd(
00627     Tcl_Interp *interp,         /* Used for looking up stuff. */
00628     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00629                                  * created by Tcl_ParseCommand. */
00630     Command *cmdPtr,            /* Points to defintion of command being
00631                                  * compiled. */
00632     CompileEnv *envPtr)         /* Holds resulting instructions. */
00633 {
00634     Tcl_Token *tokenPtr;
00635     int numWords, i;
00636     Proc *procPtr = envPtr->procPtr;
00637     DefineLineInformation;      /* TIP #280 */
00638     Tcl_Token *varTokenPtr;
00639     int dictVarIndex, nameChars;
00640     const char *name;
00641 
00642     /*
00643      * There must be at least one argument after the command.
00644      */
00645 
00646     if (parsePtr->numWords < 4 || procPtr == NULL) {
00647         return TCL_ERROR;
00648     }
00649 
00650     /*
00651      * The dictionary variable must be a local scalar that is knowable at
00652      * compile time; anything else exceeds the complexity of the opcode. So
00653      * discover what the index is.
00654      */
00655 
00656     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
00657     if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
00658         return TCL_ERROR;
00659     }
00660     name = varTokenPtr[1].start;
00661     nameChars = varTokenPtr[1].size;
00662     if (!TclIsLocalScalar(name, nameChars)) {
00663         return TCL_ERROR;
00664     }
00665     dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr);
00666 
00667     /*
00668      * Remaining words (key path and value to set) can be handled normally.
00669      */
00670 
00671     tokenPtr = TokenAfter(varTokenPtr);
00672     numWords = parsePtr->numWords-1;
00673     for (i=1 ; i<numWords ; i++) {
00674         CompileWord(envPtr, tokenPtr, interp, i);
00675         tokenPtr = TokenAfter(tokenPtr);
00676     }
00677 
00678     /*
00679      * Now emit the instruction to do the dict manipulation.
00680      */
00681 
00682     TclEmitInstInt4( INST_DICT_SET, numWords-2,         envPtr);
00683     TclEmitInt4(     dictVarIndex,                      envPtr);
00684     return TCL_OK;
00685 }
00686 
00687 int
00688 TclCompileDictIncrCmd(
00689     Tcl_Interp *interp,         /* Used for looking up stuff. */
00690     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00691                                  * created by Tcl_ParseCommand. */
00692     Command *cmdPtr,            /* Points to defintion of command being
00693                                  * compiled. */
00694     CompileEnv *envPtr)         /* Holds resulting instructions. */
00695 {
00696     Proc *procPtr = envPtr->procPtr;
00697     DefineLineInformation;      /* TIP #280 */
00698     Tcl_Token *varTokenPtr, *keyTokenPtr;
00699     int dictVarIndex, nameChars, incrAmount;
00700     const char *name;
00701 
00702     /*
00703      * There must be at least two arguments after the command.
00704      */
00705 
00706     if (parsePtr->numWords < 3 || parsePtr->numWords > 4 || procPtr == NULL) {
00707         return TCL_ERROR;
00708     }
00709     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
00710     keyTokenPtr = TokenAfter(varTokenPtr);
00711 
00712     /*
00713      * Parse the increment amount, if present.
00714      */
00715 
00716     if (parsePtr->numWords == 4) {
00717         const char *word;
00718         int numBytes, code;
00719         Tcl_Token *incrTokenPtr;
00720         Tcl_Obj *intObj;
00721 
00722         incrTokenPtr = TokenAfter(keyTokenPtr);
00723         if (incrTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
00724             return TCL_ERROR;
00725         }
00726         word = incrTokenPtr[1].start;
00727         numBytes = incrTokenPtr[1].size;
00728 
00729         intObj = Tcl_NewStringObj(word, numBytes);
00730         Tcl_IncrRefCount(intObj);
00731         code = TclGetIntFromObj(NULL, intObj, &incrAmount);
00732         TclDecrRefCount(intObj);
00733         if (code != TCL_OK) {
00734             return TCL_ERROR;
00735         }
00736     } else {
00737         incrAmount = 1;
00738     }
00739 
00740     /*
00741      * The dictionary variable must be a local scalar that is knowable at
00742      * compile time; anything else exceeds the complexity of the opcode. So
00743      * discover what the index is.
00744      */
00745 
00746     if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
00747         return TCL_ERROR;
00748     }
00749     name = varTokenPtr[1].start;
00750     nameChars = varTokenPtr[1].size;
00751     if (!TclIsLocalScalar(name, nameChars)) {
00752         return TCL_ERROR;
00753     }
00754     dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr);
00755 
00756     /*
00757      * Emit the key and the code to actually do the increment.
00758      */
00759 
00760     CompileWord(envPtr, keyTokenPtr, interp, 3);
00761     TclEmitInstInt4( INST_DICT_INCR_IMM, incrAmount,    envPtr);
00762     TclEmitInt4(     dictVarIndex,                      envPtr);
00763     return TCL_OK;
00764 }
00765 
00766 int
00767 TclCompileDictGetCmd(
00768     Tcl_Interp *interp,         /* Used for looking up stuff. */
00769     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00770                                  * created by Tcl_ParseCommand. */
00771     Command *cmdPtr,            /* Points to defintion of command being
00772                                  * compiled. */
00773     CompileEnv *envPtr)         /* Holds resulting instructions. */
00774 {
00775     Tcl_Token *tokenPtr;
00776     int numWords, i;
00777     DefineLineInformation;      /* TIP #280 */
00778 
00779     /*
00780      * There must be at least two arguments after the command (the single-arg
00781      * case is legal, but too special and magic for us to deal with here).
00782      */
00783 
00784     if (parsePtr->numWords < 3) {
00785         return TCL_ERROR;
00786     }
00787     tokenPtr = TokenAfter(parsePtr->tokenPtr);
00788     numWords = parsePtr->numWords-1;
00789 
00790     /*
00791      * Only compile this because we need INST_DICT_GET anyway.
00792      */
00793 
00794     for (i=0 ; i<numWords ; i++) {
00795         CompileWord(envPtr, tokenPtr, interp, i);
00796         tokenPtr = TokenAfter(tokenPtr);
00797     }
00798     TclEmitInstInt4(INST_DICT_GET, numWords-1, envPtr);
00799     return TCL_OK;
00800 }
00801 
00802 int
00803 TclCompileDictForCmd(
00804     Tcl_Interp *interp,         /* Used for looking up stuff. */
00805     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
00806                                  * created by Tcl_ParseCommand. */
00807     Command *cmdPtr,            /* Points to defintion of command being
00808                                  * compiled. */
00809     CompileEnv *envPtr)         /* Holds resulting instructions. */
00810 {
00811     Proc *procPtr = envPtr->procPtr;
00812     DefineLineInformation;      /* TIP #280 */
00813     Tcl_Token *varsTokenPtr, *dictTokenPtr, *bodyTokenPtr;
00814     int keyVarIndex, valueVarIndex, nameChars, loopRange, catchRange;
00815     int infoIndex, jumpDisplacement, bodyTargetOffset, emptyTargetOffset;
00816     int numVars, endTargetOffset;
00817     int savedStackDepth = envPtr->currStackDepth; /* is this necessary? */
00818     const char **argv;
00819     Tcl_DString buffer;
00820 
00821     /*
00822      * There must be at least three argument after the command.
00823      */
00824 
00825     if (parsePtr->numWords != 4 || procPtr == NULL) {
00826         return TCL_ERROR;
00827     }
00828 
00829     varsTokenPtr = TokenAfter(parsePtr->tokenPtr);
00830     dictTokenPtr = TokenAfter(varsTokenPtr);
00831     bodyTokenPtr = TokenAfter(dictTokenPtr);
00832     if (varsTokenPtr->type != TCL_TOKEN_SIMPLE_WORD ||
00833             bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
00834         return TCL_ERROR;
00835     }
00836 
00837     /*
00838      * Check we've got a pair of variables and that they are local variables.
00839      * Then extract their indices in the LVT.
00840      */
00841 
00842     Tcl_DStringInit(&buffer);
00843     Tcl_DStringAppend(&buffer, varsTokenPtr[1].start, varsTokenPtr[1].size);
00844     if (Tcl_SplitList(NULL, Tcl_DStringValue(&buffer), &numVars,
00845             &argv) != TCL_OK) {
00846         Tcl_DStringFree(&buffer);
00847         return TCL_ERROR;
00848     }
00849     Tcl_DStringFree(&buffer);
00850     if (numVars != 2) {
00851         ckfree((char *) argv);
00852         return TCL_ERROR;
00853     }
00854 
00855     nameChars = strlen(argv[0]);
00856     if (!TclIsLocalScalar(argv[0], nameChars)) {
00857         ckfree((char *) argv);
00858         return TCL_ERROR;
00859     }
00860     keyVarIndex = TclFindCompiledLocal(argv[0], nameChars, 1, procPtr);
00861 
00862     nameChars = strlen(argv[1]);
00863     if (!TclIsLocalScalar(argv[1], nameChars)) {
00864         ckfree((char *) argv);
00865         return TCL_ERROR;
00866     }
00867     valueVarIndex = TclFindCompiledLocal(argv[1], nameChars, 1, procPtr);
00868     ckfree((char *) argv);
00869 
00870     /*
00871      * Allocate a temporary variable to store the iterator reference. The
00872      * variable will contain a Tcl_DictSearch reference which will be
00873      * allocated by INST_DICT_FIRST and disposed when the variable is unset
00874      * (at which point it should also have been finished with).
00875      */
00876 
00877     infoIndex = TclFindCompiledLocal(NULL, 0, 1, procPtr);
00878 
00879     /*
00880      * Preparation complete; issue instructions. Note that this code issues
00881      * fixed-sized jumps. That simplifies things a lot!
00882      *
00883      * First up, get the dictionary and start the iteration. No catching of
00884      * errors at this point.
00885      */
00886 
00887     CompileWord(envPtr, dictTokenPtr, interp, 3);
00888     TclEmitInstInt4( INST_DICT_FIRST, infoIndex,                envPtr);
00889     emptyTargetOffset = CurrentOffset(envPtr);
00890     TclEmitInstInt4( INST_JUMP_TRUE4, 0,                        envPtr);
00891 
00892     /*
00893      * Now we catch errors from here on so that we can finalize the search
00894      * started by Tcl_DictObjFirst above.
00895      */
00896 
00897     catchRange = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
00898     TclEmitInstInt4( INST_BEGIN_CATCH4, catchRange,             envPtr);
00899     ExceptionRangeStarts(envPtr, catchRange);
00900 
00901     /*
00902      * Inside the iteration, write the loop variables.
00903      */
00904 
00905     bodyTargetOffset = CurrentOffset(envPtr);
00906     TclEmitInstInt4( INST_STORE_SCALAR4, keyVarIndex,           envPtr);
00907     TclEmitOpcode(   INST_POP,                                  envPtr);
00908     TclEmitInstInt4( INST_STORE_SCALAR4, valueVarIndex,         envPtr);
00909     TclEmitOpcode(   INST_POP,                                  envPtr);
00910 
00911     /*
00912      * Set up the loop exception targets.
00913      */
00914 
00915     loopRange = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
00916     ExceptionRangeStarts(envPtr, loopRange);
00917 
00918     /*
00919      * Compile the loop body itself. It should be stack-neutral.
00920      */
00921 
00922     envPtr->line = mapPtr->loc[eclIndex].line[4];
00923     CompileBody(envPtr, bodyTokenPtr, interp);
00924     envPtr->currStackDepth = savedStackDepth + 1;
00925     TclEmitOpcode(   INST_POP,                                  envPtr);
00926     envPtr->currStackDepth = savedStackDepth;
00927 
00928     /*
00929      * Both exception target ranges (error and loop) end here.
00930      */
00931 
00932     ExceptionRangeEnds(envPtr, loopRange);
00933     ExceptionRangeEnds(envPtr, catchRange);
00934 
00935     /*
00936      * Continue (or just normally process) by getting the next pair of items
00937      * from the dictionary and jumping back to the code to write them into
00938      * variables if there is another pair.
00939      */
00940 
00941     ExceptionRangeTarget(envPtr, loopRange, continueOffset);
00942     TclEmitInstInt4( INST_DICT_NEXT, infoIndex,                 envPtr);
00943     jumpDisplacement = bodyTargetOffset - CurrentOffset(envPtr);
00944     TclEmitInstInt4( INST_JUMP_FALSE4, jumpDisplacement,        envPtr);
00945     TclEmitOpcode(   INST_POP,                                  envPtr);
00946     TclEmitOpcode(   INST_POP,                                  envPtr);
00947 
00948     /*
00949      * Now do the final cleanup for the no-error case (this is where we break
00950      * out of the loop to) by force-terminating the iteration (if not already
00951      * terminated), ditching the exception info and jumping to the last
00952      * instruction for this command. In theory, this could be done using the
00953      * "finally" clause (next generated) but this is faster.
00954      */
00955 
00956     ExceptionRangeTarget(envPtr, loopRange, breakOffset);
00957     TclEmitInstInt4( INST_DICT_DONE, infoIndex,                 envPtr);
00958     TclEmitOpcode(   INST_END_CATCH,                            envPtr);
00959     endTargetOffset = CurrentOffset(envPtr);
00960     TclEmitInstInt4( INST_JUMP4, 0,                             envPtr);
00961 
00962     /*
00963      * Error handler "finally" clause, which force-terminates the iteration
00964      * and rethrows the error.
00965      */
00966 
00967     ExceptionRangeTarget(envPtr, catchRange, catchOffset);
00968     TclEmitOpcode(   INST_PUSH_RETURN_OPTIONS,                  envPtr);
00969     TclEmitOpcode(   INST_PUSH_RESULT,                          envPtr);
00970     TclEmitInstInt4( INST_DICT_DONE, infoIndex,                 envPtr);
00971     TclEmitOpcode(   INST_END_CATCH,                            envPtr);
00972     TclEmitOpcode(   INST_RETURN_STK,                           envPtr);
00973 
00974     /*
00975      * Otherwise we're done (the jump after the DICT_FIRST points here) and we
00976      * need to pop the bogus key/value pair (pushed to keep stack calculations
00977      * easy!) Note that we skip the END_CATCH. [Bug 1382528]
00978      */
00979 
00980     jumpDisplacement = CurrentOffset(envPtr) - emptyTargetOffset;
00981     TclUpdateInstInt4AtPc(INST_JUMP_TRUE4, jumpDisplacement,
00982             envPtr->codeStart + emptyTargetOffset);
00983     TclEmitOpcode(   INST_POP,                                  envPtr);
00984     TclEmitOpcode(   INST_POP,                                  envPtr);
00985     TclEmitInstInt4( INST_DICT_DONE, infoIndex,                 envPtr);
00986 
00987     /*
00988      * Final stage of the command (normal case) is that we push an empty
00989      * object. This is done last to promote peephole optimization when it's
00990      * dropped immediately.
00991      */
00992 
00993     jumpDisplacement = CurrentOffset(envPtr) - endTargetOffset;
00994     TclUpdateInstInt4AtPc(INST_JUMP4, jumpDisplacement,
00995             envPtr->codeStart + endTargetOffset);
00996     PushLiteral(envPtr, "", 0);
00997     return TCL_OK;
00998 }
00999 
01000 int
01001 TclCompileDictUpdateCmd(
01002     Tcl_Interp *interp,         /* Used for looking up stuff. */
01003     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
01004                                  * created by Tcl_ParseCommand. */
01005     Command *cmdPtr,            /* Points to defintion of command being
01006                                  * compiled. */
01007     CompileEnv *envPtr)         /* Holds resulting instructions. */
01008 {
01009     Proc *procPtr = envPtr->procPtr;
01010     DefineLineInformation;      /* TIP #280 */
01011     const char *name;
01012     int i, nameChars, dictIndex, numVars, range, infoIndex;
01013     Tcl_Token **keyTokenPtrs, *dictVarTokenPtr, *bodyTokenPtr, *tokenPtr;
01014     DictUpdateInfo *duiPtr;
01015     JumpFixup jumpFixup;
01016 
01017     /*
01018      * There must be at least one argument after the command.
01019      */
01020 
01021     if (parsePtr->numWords < 5 || procPtr == NULL) {
01022         return TCL_ERROR;
01023     }
01024 
01025     /*
01026      * Parse the command. Expect the following:
01027      *   dict update <lit(eral)> <any> <lit> ?<any> <lit> ...? <lit>
01028      */
01029 
01030     if ((parsePtr->numWords - 1) & 1) {
01031         return TCL_ERROR;
01032     }
01033     numVars = (parsePtr->numWords - 3) / 2;
01034 
01035     /*
01036      * The dictionary variable must be a local scalar that is knowable at
01037      * compile time; anything else exceeds the complexity of the opcode. So
01038      * discover what the index is.
01039      */
01040 
01041     dictVarTokenPtr = TokenAfter(parsePtr->tokenPtr);
01042     if (dictVarTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01043         return TCL_ERROR;
01044     }
01045     name = dictVarTokenPtr[1].start;
01046     nameChars = dictVarTokenPtr[1].size;
01047     if (!TclIsLocalScalar(name, nameChars)) {
01048         return TCL_ERROR;
01049     }
01050     dictIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr);
01051 
01052     /*
01053      * Assemble the instruction metadata. This is complex enough that it is
01054      * represented as auxData; it holds an ordered list of variable indices
01055      * that are to be used.
01056      */
01057 
01058     duiPtr = (DictUpdateInfo *)
01059             ckalloc(sizeof(DictUpdateInfo) + sizeof(int) * (numVars - 1));
01060     duiPtr->length = numVars;
01061     keyTokenPtrs = (Tcl_Token **) TclStackAlloc(interp,
01062             sizeof(Tcl_Token *) * numVars);
01063     tokenPtr = TokenAfter(dictVarTokenPtr);
01064 
01065     for (i=0 ; i<numVars ; i++) {
01066         /*
01067          * Put keys to one side for later compilation to bytecode.
01068          */
01069 
01070         keyTokenPtrs[i] = tokenPtr;
01071 
01072         /*
01073          * Variables first need to be checked for sanity.
01074          */
01075 
01076         tokenPtr = TokenAfter(tokenPtr);
01077         if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01078             ckfree((char *) duiPtr);
01079             TclStackFree(interp, keyTokenPtrs);
01080             return TCL_ERROR;
01081         }
01082         name = tokenPtr[1].start;
01083         nameChars = tokenPtr[1].size;
01084         if (!TclIsLocalScalar(name, nameChars)) {
01085             ckfree((char *) duiPtr);
01086             TclStackFree(interp, keyTokenPtrs);
01087             return TCL_ERROR;
01088         }
01089 
01090         /*
01091          * Stash the index in the auxiliary data.
01092          */
01093 
01094         duiPtr->varIndices[i] =
01095                 TclFindCompiledLocal(name, nameChars, 1, procPtr);
01096         tokenPtr = TokenAfter(tokenPtr);
01097     }
01098     if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01099         ckfree((char *) duiPtr);
01100         TclStackFree(interp, keyTokenPtrs);
01101         return TCL_ERROR;
01102     }
01103     bodyTokenPtr = tokenPtr;
01104 
01105     /*
01106      * The list of variables to bind is stored in auxiliary data so that it
01107      * can't be snagged by literal sharing and forced to shimmer dangerously.
01108      */
01109 
01110     infoIndex = TclCreateAuxData(duiPtr, &tclDictUpdateInfoType, envPtr);
01111 
01112     for (i=0 ; i<numVars ; i++) {
01113         CompileWord(envPtr, keyTokenPtrs[i], interp, i);
01114     }
01115     TclEmitInstInt4( INST_LIST, numVars,                        envPtr);
01116     TclEmitInstInt4( INST_DICT_UPDATE_START, dictIndex,         envPtr);
01117     TclEmitInt4(     infoIndex,                                 envPtr);
01118 
01119     range = DeclareExceptionRange(envPtr, CATCH_EXCEPTION_RANGE);
01120     TclEmitInstInt4( INST_BEGIN_CATCH4, range,                  envPtr);
01121 
01122     ExceptionRangeStarts(envPtr, range);
01123     CompileBody(envPtr, bodyTokenPtr, interp);
01124     ExceptionRangeEnds(envPtr, range);
01125 
01126     /*
01127      * Normal termination code: the stack has the key list below the result of
01128      * the body evaluation: swap them and finish the update code.
01129      */
01130 
01131     TclEmitOpcode(   INST_END_CATCH,                            envPtr);
01132     TclEmitInstInt4( INST_REVERSE, 2,                           envPtr);
01133     TclEmitInstInt4( INST_DICT_UPDATE_END, dictIndex,           envPtr);
01134     TclEmitInt4(     infoIndex,                                 envPtr);
01135 
01136     /*
01137      * Jump around the exceptional termination code.
01138      */
01139 
01140     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpFixup);
01141 
01142     /*
01143      * Termination code for non-ok returns: stash the result and return
01144      * options in the stack, bring up the key list, finish the update code,
01145      * and finally return with the catched return data
01146      */
01147 
01148     ExceptionRangeTarget(envPtr, range, catchOffset);
01149     TclEmitOpcode(   INST_PUSH_RESULT,                          envPtr);
01150     TclEmitOpcode(   INST_PUSH_RETURN_OPTIONS,                  envPtr);
01151     TclEmitOpcode(   INST_END_CATCH,                            envPtr);
01152     TclEmitInstInt4( INST_REVERSE, 3,                           envPtr);
01153 
01154     TclEmitInstInt4( INST_DICT_UPDATE_END, dictIndex,           envPtr);
01155     TclEmitInt4(     infoIndex,                                 envPtr);
01156     TclEmitOpcode(   INST_RETURN_STK,                           envPtr);
01157 
01158     if (TclFixupForwardJumpToHere(envPtr, &jumpFixup, 127)) {
01159         Tcl_Panic("TclCompileDictCmd(update): bad jump distance %d",
01160                 CurrentOffset(envPtr) - jumpFixup.codeOffset);
01161     }
01162     TclStackFree(interp, keyTokenPtrs);
01163     return TCL_OK;
01164 }
01165 
01166 int
01167 TclCompileDictAppendCmd(
01168     Tcl_Interp *interp,         /* Used for looking up stuff. */
01169     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
01170                                  * created by Tcl_ParseCommand. */
01171     Command *cmdPtr,            /* Points to defintion of command being
01172                                  * compiled. */
01173     CompileEnv *envPtr)         /* Holds resulting instructions. */
01174 {
01175     Proc *procPtr = envPtr->procPtr;
01176     DefineLineInformation;      /* TIP #280 */
01177     Tcl_Token *tokenPtr;
01178     int i, dictVarIndex;
01179 
01180     /*
01181      * There must be at least two argument after the command. And we impose an
01182      * (arbirary) safe limit; anyone exceeding it should stop worrying about
01183      * speed quite so much. ;-)
01184      */
01185 
01186     if (parsePtr->numWords<4 || parsePtr->numWords>100 || procPtr==NULL) {
01187         return TCL_ERROR;
01188     }
01189 
01190     /*
01191      * Get the index of the local variable that we will be working with.
01192      */
01193 
01194     tokenPtr = TokenAfter(parsePtr->tokenPtr);
01195     if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01196         return TCL_ERROR;
01197     } else {
01198         register const char *name = tokenPtr[1].start;
01199         register int nameChars = tokenPtr[1].size;
01200 
01201         if (!TclIsLocalScalar(name, nameChars)) {
01202             return TCL_ERROR;
01203         }
01204         dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr);
01205     }
01206 
01207     /*
01208      * Produce the string to concatenate onto the dictionary entry.
01209      */
01210 
01211     tokenPtr = TokenAfter(tokenPtr);
01212     for (i=2 ; i<parsePtr->numWords ; i++) {
01213         CompileWord(envPtr, tokenPtr, interp, i);
01214         tokenPtr = TokenAfter(tokenPtr);
01215     }
01216     if (parsePtr->numWords > 4) {
01217         TclEmitInstInt1(INST_CONCAT1, parsePtr->numWords-2, envPtr);
01218     }
01219 
01220     /*
01221      * Do the concatenation.
01222      */
01223 
01224     TclEmitInstInt4(INST_DICT_APPEND, dictVarIndex, envPtr);
01225     return TCL_OK;
01226 }
01227 
01228 int
01229 TclCompileDictLappendCmd(
01230     Tcl_Interp *interp,         /* Used for looking up stuff. */
01231     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
01232                                  * created by Tcl_ParseCommand. */
01233     Command *cmdPtr,            /* Points to defintion of command being
01234                                  * compiled. */
01235     CompileEnv *envPtr)         /* Holds resulting instructions. */
01236 {
01237     Proc *procPtr = envPtr->procPtr;
01238     DefineLineInformation;      /* TIP #280 */
01239     Tcl_Token *varTokenPtr, *keyTokenPtr, *valueTokenPtr;
01240     int dictVarIndex, nameChars;
01241     const char *name;
01242 
01243     /*
01244      * There must be three arguments after the command.
01245      */
01246 
01247     if (parsePtr->numWords != 4 || procPtr == NULL) {
01248         return TCL_ERROR;
01249     }
01250 
01251     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
01252     keyTokenPtr = TokenAfter(varTokenPtr);
01253     valueTokenPtr = TokenAfter(keyTokenPtr);
01254     if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01255         return TCL_ERROR;
01256     }
01257     name = varTokenPtr[1].start;
01258     nameChars = varTokenPtr[1].size;
01259     if (!TclIsLocalScalar(name, nameChars)) {
01260         return TCL_ERROR;
01261     }
01262     dictVarIndex = TclFindCompiledLocal(name, nameChars, 1, procPtr);
01263     CompileWord(envPtr, keyTokenPtr, interp, 3);
01264     CompileWord(envPtr, valueTokenPtr, interp, 4);
01265     TclEmitInstInt4( INST_DICT_LAPPEND, dictVarIndex, envPtr);
01266     return TCL_OK;
01267 }
01268 
01269 /*
01270  *----------------------------------------------------------------------
01271  *
01272  * DupDictUpdateInfo, FreeDictUpdateInfo --
01273  *
01274  *      Functions to duplicate, release and print the aux data created for use
01275  *      with the INST_DICT_UPDATE_START and INST_DICT_UPDATE_END instructions.
01276  *
01277  * Results:
01278  *      DupDictUpdateInfo: a copy of the auxiliary data
01279  *      FreeDictUpdateInfo: none
01280  *      PrintDictUpdateInfo: none
01281  *
01282  * Side effects:
01283  *      DupDictUpdateInfo: allocates memory
01284  *      FreeDictUpdateInfo: releases memory
01285  *      PrintDictUpdateInfo: none
01286  *
01287  *----------------------------------------------------------------------
01288  */
01289 
01290 static ClientData
01291 DupDictUpdateInfo(
01292     ClientData clientData)
01293 {
01294     DictUpdateInfo *dui1Ptr, *dui2Ptr;
01295     unsigned len;
01296 
01297     dui1Ptr = clientData;
01298     len = sizeof(DictUpdateInfo) + sizeof(int) * (dui1Ptr->length - 1);
01299     dui2Ptr = (DictUpdateInfo *) ckalloc(len);
01300     memcpy(dui2Ptr, dui1Ptr, len);
01301     return dui2Ptr;
01302 }
01303 
01304 static void
01305 FreeDictUpdateInfo(
01306     ClientData clientData)
01307 {
01308     ckfree(clientData);
01309 }
01310 
01311 static void
01312 PrintDictUpdateInfo(
01313     ClientData clientData,
01314     Tcl_Obj *appendObj,
01315     ByteCode *codePtr,
01316     unsigned int pcOffset)
01317 {
01318     DictUpdateInfo *duiPtr = clientData;
01319     int i;
01320 
01321     for (i=0 ; i<duiPtr->length ; i++) {
01322         if (i) {
01323             Tcl_AppendToObj(appendObj, ", ", -1);
01324         }
01325         Tcl_AppendPrintfToObj(appendObj, "%%v%u", duiPtr->varIndices[i]);
01326     }
01327 }
01328 
01329 /*
01330  *----------------------------------------------------------------------
01331  *
01332  * TclCompileExprCmd --
01333  *
01334  *      Procedure called to compile the "expr" command.
01335  *
01336  * Results:
01337  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
01338  *      evaluation to runtime.
01339  *
01340  * Side effects:
01341  *      Instructions are added to envPtr to execute the "expr" command at
01342  *      runtime.
01343  *
01344  *----------------------------------------------------------------------
01345  */
01346 
01347 int
01348 TclCompileExprCmd(
01349     Tcl_Interp *interp,         /* Used for error reporting. */
01350     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
01351                                  * created by Tcl_ParseCommand. */
01352     Command *cmdPtr,            /* Points to defintion of command being
01353                                  * compiled. */
01354     CompileEnv *envPtr)         /* Holds resulting instructions. */
01355 {
01356     Tcl_Token *firstWordPtr;
01357 
01358     if (parsePtr->numWords == 1) {
01359         return TCL_ERROR;
01360     }
01361 
01362     /*
01363      * TIP #280: Use the per-word line information of the current command.
01364      */
01365 
01366     envPtr->line = envPtr->extCmdMapPtr->loc[
01367             envPtr->extCmdMapPtr->nuloc-1].line[1];
01368 
01369     firstWordPtr = TokenAfter(parsePtr->tokenPtr);
01370     TclCompileExprWords(interp, firstWordPtr, parsePtr->numWords-1, envPtr);
01371     return TCL_OK;
01372 }
01373 
01374 /*
01375  *----------------------------------------------------------------------
01376  *
01377  * TclCompileForCmd --
01378  *
01379  *      Procedure called to compile the "for" command.
01380  *
01381  * Results:
01382  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
01383  *      evaluation to runtime.
01384  *
01385  * Side effects:
01386  *      Instructions are added to envPtr to execute the "for" command at
01387  *      runtime.
01388  *
01389  *----------------------------------------------------------------------
01390  */
01391 
01392 int
01393 TclCompileForCmd(
01394     Tcl_Interp *interp,         /* Used for error reporting. */
01395     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
01396                                  * created by Tcl_ParseCommand. */
01397     Command *cmdPtr,            /* Points to defintion of command being
01398                                  * compiled. */
01399     CompileEnv *envPtr)         /* Holds resulting instructions. */
01400 {
01401     Tcl_Token *startTokenPtr, *testTokenPtr, *nextTokenPtr, *bodyTokenPtr;
01402     JumpFixup jumpEvalCondFixup;
01403     int testCodeOffset, bodyCodeOffset, nextCodeOffset, jumpDist;
01404     int bodyRange, nextRange;
01405     int savedStackDepth = envPtr->currStackDepth;
01406     DefineLineInformation;      /* TIP #280 */
01407 
01408     if (parsePtr->numWords != 5) {
01409         return TCL_ERROR;
01410     }
01411 
01412     /*
01413      * If the test expression requires substitutions, don't compile the for
01414      * command inline. E.g., the expression might cause the loop to never
01415      * execute or execute forever, as in "for {} "$x > 5" {incr x} {}".
01416      */
01417 
01418     startTokenPtr = TokenAfter(parsePtr->tokenPtr);
01419     testTokenPtr = TokenAfter(startTokenPtr);
01420     if (testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01421         return TCL_ERROR;
01422     }
01423 
01424     /*
01425      * Bail out also if the body or the next expression require substitutions
01426      * in order to insure correct behaviour [Bug 219166]
01427      */
01428 
01429     nextTokenPtr = TokenAfter(testTokenPtr);
01430     bodyTokenPtr = TokenAfter(nextTokenPtr);
01431     if ((nextTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)
01432             || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
01433         return TCL_ERROR;
01434     }
01435 
01436     /*
01437      * Create ExceptionRange records for the body and the "next" command. The
01438      * "next" command's ExceptionRange supports break but not continue (and
01439      * has a -1 continueOffset).
01440      */
01441 
01442     bodyRange = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
01443     nextRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
01444 
01445     /*
01446      * Inline compile the initial command.
01447      */
01448 
01449     envPtr->line = mapPtr->loc[eclIndex].line[1];
01450     CompileBody(envPtr, startTokenPtr, interp);
01451     TclEmitOpcode(INST_POP, envPtr);
01452 
01453     /*
01454      * Jump to the evaluation of the condition. This code uses the "loop
01455      * rotation" optimisation (which eliminates one branch from the loop).
01456      * "for start cond next body" produces then:
01457      *       start
01458      *       goto A
01459      *    B: body                : bodyCodeOffset
01460      *       next                : nextCodeOffset, continueOffset
01461      *    A: cond -> result      : testCodeOffset
01462      *       if (result) goto B
01463      */
01464 
01465     TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup);
01466 
01467     /*
01468      * Compile the loop body.
01469      */
01470 
01471     bodyCodeOffset = ExceptionRangeStarts(envPtr, bodyRange);
01472     envPtr->line = mapPtr->loc[eclIndex].line[4];
01473     CompileBody(envPtr, bodyTokenPtr, interp);
01474     ExceptionRangeEnds(envPtr, bodyRange);
01475     envPtr->currStackDepth = savedStackDepth + 1;
01476     TclEmitOpcode(INST_POP, envPtr);
01477 
01478     /*
01479      * Compile the "next" subcommand.
01480      */
01481 
01482     envPtr->currStackDepth = savedStackDepth;
01483     nextCodeOffset = ExceptionRangeStarts(envPtr, nextRange);
01484     envPtr->line = mapPtr->loc[eclIndex].line[3];
01485     CompileBody(envPtr, nextTokenPtr, interp);
01486     ExceptionRangeEnds(envPtr, nextRange);
01487     envPtr->currStackDepth = savedStackDepth + 1;
01488     TclEmitOpcode(INST_POP, envPtr);
01489     envPtr->currStackDepth = savedStackDepth;
01490 
01491     /*
01492      * Compile the test expression then emit the conditional jump that
01493      * terminates the for.
01494      */
01495 
01496     testCodeOffset = CurrentOffset(envPtr);
01497 
01498     jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
01499     if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
01500         bodyCodeOffset += 3;
01501         nextCodeOffset += 3;
01502         testCodeOffset += 3;
01503     }
01504 
01505     envPtr->line = mapPtr->loc[eclIndex].line[2];
01506     envPtr->currStackDepth = savedStackDepth;
01507     TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
01508     envPtr->currStackDepth = savedStackDepth + 1;
01509 
01510     jumpDist = CurrentOffset(envPtr) - bodyCodeOffset;
01511     if (jumpDist > 127) {
01512         TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
01513     } else {
01514         TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
01515     }
01516 
01517     /*
01518      * Fix the starting points of the exception ranges (may have moved due to
01519      * jump type modification) and set where the exceptions target.
01520      */
01521 
01522     envPtr->exceptArrayPtr[bodyRange].codeOffset = bodyCodeOffset;
01523     envPtr->exceptArrayPtr[bodyRange].continueOffset = nextCodeOffset;
01524 
01525     envPtr->exceptArrayPtr[nextRange].codeOffset = nextCodeOffset;
01526 
01527     ExceptionRangeTarget(envPtr, bodyRange, breakOffset);
01528     ExceptionRangeTarget(envPtr, nextRange, breakOffset);
01529 
01530     /*
01531      * The for command's result is an empty string.
01532      */
01533 
01534     envPtr->currStackDepth = savedStackDepth;
01535     PushLiteral(envPtr, "", 0);
01536 
01537     return TCL_OK;
01538 }
01539 
01540 /*
01541  *----------------------------------------------------------------------
01542  *
01543  * TclCompileForeachCmd --
01544  *
01545  *      Procedure called to compile the "foreach" command.
01546  *
01547  * Results:
01548  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
01549  *      evaluation to runtime.
01550  *
01551  * Side effects:
01552  *      Instructions are added to envPtr to execute the "foreach" command at
01553  *      runtime.
01554  *
01555  *----------------------------------------------------------------------
01556  */
01557 
01558 int
01559 TclCompileForeachCmd(
01560     Tcl_Interp *interp,         /* Used for error reporting. */
01561     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
01562                                  * created by Tcl_ParseCommand. */
01563     Command *cmdPtr,            /* Points to defintion of command being
01564                                  * compiled. */
01565     CompileEnv *envPtr)         /* Holds resulting instructions. */
01566 {
01567     Proc *procPtr = envPtr->procPtr;
01568     ForeachInfo *infoPtr;       /* Points to the structure describing this
01569                                  * foreach command. Stored in a AuxData
01570                                  * record in the ByteCode. */
01571     int firstValueTemp;         /* Index of the first temp var in the frame
01572                                  * used to point to a value list. */
01573     int loopCtTemp;             /* Index of temp var holding the loop's
01574                                  * iteration count. */
01575     Tcl_Token *tokenPtr, *bodyTokenPtr;
01576     unsigned char *jumpPc;
01577     JumpFixup jumpFalseFixup;
01578     int jumpBackDist, jumpBackOffset, infoIndex, range, bodyIndex;
01579     int numWords, numLists, numVars, loopIndex, tempVar, i, j, code;
01580     int savedStackDepth = envPtr->currStackDepth;
01581     DefineLineInformation;      /* TIP #280 */
01582 
01583     /*
01584      * We parse the variable list argument words and create two arrays:
01585      *    varcList[i] is number of variables in i-th var list.
01586      *    varvList[i] points to array of var names in i-th var list.
01587      */
01588 
01589     int *varcList;
01590     const char ***varvList;
01591 
01592     /*
01593      * If the foreach command isn't in a procedure, don't compile it inline:
01594      * the payoff is too small.
01595      */
01596 
01597     if (procPtr == NULL) {
01598         return TCL_ERROR;
01599     }
01600 
01601     numWords = parsePtr->numWords;
01602     if ((numWords < 4) || (numWords%2 != 0)) {
01603         return TCL_ERROR;
01604     }
01605 
01606     /*
01607      * Bail out if the body requires substitutions in order to insure correct
01608      * behaviour. [Bug 219166]
01609      */
01610 
01611     for (i = 0, tokenPtr = parsePtr->tokenPtr; i < numWords-1; i++) {
01612         tokenPtr = TokenAfter(tokenPtr);
01613     }
01614     bodyTokenPtr = tokenPtr;
01615     if (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01616         return TCL_ERROR;
01617     }
01618 
01619     bodyIndex = i-1;
01620 
01621     /*
01622      * Allocate storage for the varcList and varvList arrays if necessary.
01623      */
01624 
01625     numLists = (numWords - 2)/2;
01626     varcList = (int *) TclStackAlloc(interp, numLists * sizeof(int));
01627     memset(varcList, 0, numLists * sizeof(int));
01628     varvList = (const char ***) TclStackAlloc(interp,
01629             numLists * sizeof(const char **));
01630     memset((char*) varvList, 0, numLists * sizeof(const char **));
01631 
01632     /*
01633      * Break up each var list and set the varcList and varvList arrays. Don't
01634      * compile the foreach inline if any var name needs substitutions or isn't
01635      * a scalar, or if any var list needs substitutions.
01636      */
01637 
01638     loopIndex = 0;
01639     for (i = 0, tokenPtr = parsePtr->tokenPtr;
01640             i < numWords-1;
01641             i++, tokenPtr = TokenAfter(tokenPtr)) {
01642         Tcl_DString varList;
01643 
01644         if (i%2 != 1) {
01645             continue;
01646         }
01647         if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
01648             code = TCL_ERROR;
01649             goto done;
01650         }
01651 
01652         /*
01653          * Lots of copying going on here. Need a ListObj wizard to show a
01654          * better way.
01655          */
01656 
01657         Tcl_DStringInit(&varList);
01658         Tcl_DStringAppend(&varList, tokenPtr[1].start, tokenPtr[1].size);
01659         code = Tcl_SplitList(interp, Tcl_DStringValue(&varList),
01660                 &varcList[loopIndex], &varvList[loopIndex]);
01661         Tcl_DStringFree(&varList);
01662         if (code != TCL_OK) {
01663             code = TCL_ERROR;
01664             goto done;
01665         }
01666         numVars = varcList[loopIndex];
01667 
01668         /*
01669          * If the variable list is empty, we can enter an infinite loop when
01670          * the interpreted version would not. Take care to ensure this does
01671          * not happen. [Bug 1671138]
01672          */
01673 
01674         if (numVars == 0) {
01675             code = TCL_ERROR;
01676             goto done;
01677         }
01678 
01679         for (j = 0;  j < numVars;  j++) {
01680             const char *varName = varvList[loopIndex][j];
01681 
01682             if (!TclIsLocalScalar(varName, (int) strlen(varName))) {
01683                 code = TCL_ERROR;
01684                 goto done;
01685             }
01686         }
01687         loopIndex++;
01688     }
01689 
01690     /*
01691      * We will compile the foreach command. Reserve (numLists + 1) temporary
01692      * variables:
01693      *    - numLists temps to hold each value list
01694      *    - 1 temp for the loop counter (index of next element in each list)
01695      *
01696      * At this time we don't try to reuse temporaries; if there are two
01697      * nonoverlapping foreach loops, they don't share any temps.
01698      */
01699 
01700     code = TCL_OK;
01701     firstValueTemp = -1;
01702     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
01703         tempVar = TclFindCompiledLocal(NULL, /*nameChars*/ 0,
01704                 /*create*/ 1, procPtr);
01705         if (loopIndex == 0) {
01706             firstValueTemp = tempVar;
01707         }
01708     }
01709     loopCtTemp = TclFindCompiledLocal(NULL, /*nameChars*/ 0,
01710             /*create*/ 1, procPtr);
01711 
01712     /*
01713      * Create and initialize the ForeachInfo and ForeachVarList data
01714      * structures describing this command. Then create a AuxData record
01715      * pointing to the ForeachInfo structure.
01716      */
01717 
01718     infoPtr = (ForeachInfo *) ckalloc((unsigned)
01719             sizeof(ForeachInfo) + numLists*sizeof(ForeachVarList *));
01720     infoPtr->numLists = numLists;
01721     infoPtr->firstValueTemp = firstValueTemp;
01722     infoPtr->loopCtTemp = loopCtTemp;
01723     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
01724         ForeachVarList *varListPtr;
01725         numVars = varcList[loopIndex];
01726         varListPtr = (ForeachVarList *) ckalloc((unsigned)
01727                 sizeof(ForeachVarList) + numVars*sizeof(int));
01728         varListPtr->numVars = numVars;
01729         for (j = 0;  j < numVars;  j++) {
01730             const char *varName = varvList[loopIndex][j];
01731             int nameChars = strlen(varName);
01732 
01733             varListPtr->varIndexes[j] = TclFindCompiledLocal(varName,
01734                     nameChars, /*create*/ 1, procPtr);
01735         }
01736         infoPtr->varLists[loopIndex] = varListPtr;
01737     }
01738     infoIndex = TclCreateAuxData(infoPtr, &tclForeachInfoType, envPtr);
01739 
01740     /*
01741      * Create an exception record to handle [break] and [continue].
01742      */
01743 
01744     range = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
01745 
01746     /*
01747      * Evaluate then store each value list in the associated temporary.
01748      */
01749 
01750     loopIndex = 0;
01751     for (i = 0, tokenPtr = parsePtr->tokenPtr;
01752             i < numWords-1;
01753             i++, tokenPtr = TokenAfter(tokenPtr)) {
01754         if ((i%2 == 0) && (i > 0)) {
01755             envPtr->line = mapPtr->loc[eclIndex].line[i];
01756             CompileTokens(envPtr, tokenPtr, interp);
01757             tempVar = (firstValueTemp + loopIndex);
01758             if (tempVar <= 255) {
01759                 TclEmitInstInt1(INST_STORE_SCALAR1, tempVar, envPtr);
01760             } else {
01761                 TclEmitInstInt4(INST_STORE_SCALAR4, tempVar, envPtr);
01762             }
01763             TclEmitOpcode(INST_POP, envPtr);
01764             loopIndex++;
01765         }
01766     }
01767 
01768     /*
01769      * Initialize the temporary var that holds the count of loop iterations.
01770      */
01771 
01772     TclEmitInstInt4(INST_FOREACH_START4, infoIndex, envPtr);
01773 
01774     /*
01775      * Top of loop code: assign each loop variable and check whether
01776      * to terminate the loop.
01777      */
01778 
01779     ExceptionRangeTarget(envPtr, range, continueOffset);
01780     TclEmitInstInt4(INST_FOREACH_STEP4, infoIndex, envPtr);
01781     TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, &jumpFalseFixup);
01782 
01783     /*
01784      * Inline compile the loop body.
01785      */
01786 
01787     envPtr->line = mapPtr->loc[eclIndex].line[bodyIndex];
01788     ExceptionRangeStarts(envPtr, range);
01789     CompileBody(envPtr, bodyTokenPtr, interp);
01790     ExceptionRangeEnds(envPtr, range);
01791     envPtr->currStackDepth = savedStackDepth + 1;
01792     TclEmitOpcode(INST_POP, envPtr);
01793 
01794     /*
01795      * Jump back to the test at the top of the loop. Generate a 4 byte jump if
01796      * the distance to the test is > 120 bytes. This is conservative and
01797      * ensures that we won't have to replace this jump if we later need to
01798      * replace the ifFalse jump with a 4 byte jump.
01799      */
01800 
01801     jumpBackOffset = CurrentOffset(envPtr);
01802     jumpBackDist = jumpBackOffset-envPtr->exceptArrayPtr[range].continueOffset;
01803     if (jumpBackDist > 120) {
01804         TclEmitInstInt4(INST_JUMP4, -jumpBackDist, envPtr);
01805     } else {
01806         TclEmitInstInt1(INST_JUMP1, -jumpBackDist, envPtr);
01807     }
01808 
01809     /*
01810      * Fix the target of the jump after the foreach_step test.
01811      */
01812 
01813     if (TclFixupForwardJumpToHere(envPtr, &jumpFalseFixup, 127)) {
01814         /*
01815          * Update the loop body's starting PC offset since it moved down.
01816          */
01817 
01818         envPtr->exceptArrayPtr[range].codeOffset += 3;
01819 
01820         /*
01821          * Update the jump back to the test at the top of the loop since it
01822          * also moved down 3 bytes.
01823          */
01824 
01825         jumpBackOffset += 3;
01826         jumpPc = (envPtr->codeStart + jumpBackOffset);
01827         jumpBackDist += 3;
01828         if (jumpBackDist > 120) {
01829             TclUpdateInstInt4AtPc(INST_JUMP4, -jumpBackDist, jumpPc);
01830         } else {
01831             TclUpdateInstInt1AtPc(INST_JUMP1, -jumpBackDist, jumpPc);
01832         }
01833     }
01834 
01835     /*
01836      * Set the loop's break target.
01837      */
01838 
01839     ExceptionRangeTarget(envPtr, range, breakOffset);
01840 
01841     /*
01842      * The foreach command's result is an empty string.
01843      */
01844 
01845     envPtr->currStackDepth = savedStackDepth;
01846     PushLiteral(envPtr, "", 0);
01847     envPtr->currStackDepth = savedStackDepth + 1;
01848 
01849   done:
01850     for (loopIndex = 0;  loopIndex < numLists;  loopIndex++) {
01851         if (varvList[loopIndex] != NULL) {
01852             ckfree((char *) varvList[loopIndex]);
01853         }
01854     }
01855     TclStackFree(interp, (void *)varvList);
01856     TclStackFree(interp, varcList);
01857     return code;
01858 }
01859 
01860 /*
01861  *----------------------------------------------------------------------
01862  *
01863  * DupForeachInfo --
01864  *
01865  *      This procedure duplicates a ForeachInfo structure created as auxiliary
01866  *      data during the compilation of a foreach command.
01867  *
01868  * Results:
01869  *      A pointer to a newly allocated copy of the existing ForeachInfo
01870  *      structure is returned.
01871  *
01872  * Side effects:
01873  *      Storage for the copied ForeachInfo record is allocated. If the
01874  *      original ForeachInfo structure pointed to any ForeachVarList records,
01875  *      these structures are also copied and pointers to them are stored in
01876  *      the new ForeachInfo record.
01877  *
01878  *----------------------------------------------------------------------
01879  */
01880 
01881 static ClientData
01882 DupForeachInfo(
01883     ClientData clientData)      /* The foreach command's compilation auxiliary
01884                                  * data to duplicate. */
01885 {
01886     register ForeachInfo *srcPtr = clientData;
01887     ForeachInfo *dupPtr;
01888     register ForeachVarList *srcListPtr, *dupListPtr;
01889     int numVars, i, j, numLists = srcPtr->numLists;
01890 
01891     dupPtr = (ForeachInfo *) ckalloc((unsigned)
01892             sizeof(ForeachInfo) + numLists*sizeof(ForeachVarList *));
01893     dupPtr->numLists = numLists;
01894     dupPtr->firstValueTemp = srcPtr->firstValueTemp;
01895     dupPtr->loopCtTemp = srcPtr->loopCtTemp;
01896 
01897     for (i = 0;  i < numLists;  i++) {
01898         srcListPtr = srcPtr->varLists[i];
01899         numVars = srcListPtr->numVars;
01900         dupListPtr = (ForeachVarList *) ckalloc((unsigned)
01901                 sizeof(ForeachVarList) + numVars*sizeof(int));
01902         dupListPtr->numVars = numVars;
01903         for (j = 0;  j < numVars;  j++) {
01904             dupListPtr->varIndexes[j] = srcListPtr->varIndexes[j];
01905         }
01906         dupPtr->varLists[i] = dupListPtr;
01907     }
01908     return dupPtr;
01909 }
01910 
01911 /*
01912  *----------------------------------------------------------------------
01913  *
01914  * FreeForeachInfo --
01915  *
01916  *      Procedure to free a ForeachInfo structure created as auxiliary data
01917  *      during the compilation of a foreach command.
01918  *
01919  * Results:
01920  *      None.
01921  *
01922  * Side effects:
01923  *      Storage for the ForeachInfo structure pointed to by the ClientData
01924  *      argument is freed as is any ForeachVarList record pointed to by the
01925  *      ForeachInfo structure.
01926  *
01927  *----------------------------------------------------------------------
01928  */
01929 
01930 static void
01931 FreeForeachInfo(
01932     ClientData clientData)      /* The foreach command's compilation auxiliary
01933                                  * data to free. */
01934 {
01935     register ForeachInfo *infoPtr = clientData;
01936     register ForeachVarList *listPtr;
01937     int numLists = infoPtr->numLists;
01938     register int i;
01939 
01940     for (i = 0;  i < numLists;  i++) {
01941         listPtr = infoPtr->varLists[i];
01942         ckfree((char *) listPtr);
01943     }
01944     ckfree((char *) infoPtr);
01945 }
01946 
01947 /*
01948  *----------------------------------------------------------------------
01949  *
01950  * PrintForeachInfo --
01951  *
01952  *      Function to write a human-readable representation of a ForeachInfo
01953  *      structure to stdout for debugging.
01954  *
01955  * Results:
01956  *      None.
01957  *
01958  * Side effects:
01959  *      None.
01960  *
01961  *----------------------------------------------------------------------
01962  */
01963 
01964 static void
01965 PrintForeachInfo(
01966     ClientData clientData,
01967     Tcl_Obj *appendObj,
01968     ByteCode *codePtr,
01969     unsigned int pcOffset)
01970 {
01971     register ForeachInfo *infoPtr = clientData;
01972     register ForeachVarList *varsPtr;
01973     int i, j;
01974 
01975     Tcl_AppendToObj(appendObj, "data=[", -1);
01976 
01977     for (i=0 ; i<infoPtr->numLists ; i++) {
01978         if (i) {
01979             Tcl_AppendToObj(appendObj, ", ", -1);
01980         }
01981         Tcl_AppendPrintfToObj(appendObj, "%%v%u",
01982                 (unsigned) (infoPtr->firstValueTemp + i));
01983     }
01984     Tcl_AppendPrintfToObj(appendObj, "], loop=%%v%u",
01985             (unsigned) infoPtr->loopCtTemp);
01986     for (i=0 ; i<infoPtr->numLists ; i++) {
01987         if (i) {
01988             Tcl_AppendToObj(appendObj, ",", -1);
01989         }
01990         Tcl_AppendPrintfToObj(appendObj, "\n\t\t it%%v%u\t[",
01991                 (unsigned) (infoPtr->firstValueTemp + i));
01992         varsPtr = infoPtr->varLists[i];
01993         for (j=0 ; j<varsPtr->numVars ; j++) {
01994             if (j) {
01995                 Tcl_AppendToObj(appendObj, ", ", -1);
01996             }
01997             Tcl_AppendPrintfToObj(appendObj, "%%v%u",
01998                     (unsigned) varsPtr->varIndexes[j]);
01999         }
02000         Tcl_AppendToObj(appendObj, "]", -1);
02001     }
02002 }
02003 
02004 /*
02005  *----------------------------------------------------------------------
02006  *
02007  * TclCompileIfCmd --
02008  *
02009  *      Procedure called to compile the "if" command.
02010  *
02011  * Results:
02012  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02013  *      evaluation to runtime.
02014  *
02015  * Side effects:
02016  *      Instructions are added to envPtr to execute the "if" command at
02017  *      runtime.
02018  *
02019  *----------------------------------------------------------------------
02020  */
02021 
02022 int
02023 TclCompileIfCmd(
02024     Tcl_Interp *interp,         /* Used for error reporting. */
02025     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02026                                  * created by Tcl_ParseCommand. */
02027     Command *cmdPtr,            /* Points to defintion of command being
02028                                  * compiled. */
02029     CompileEnv *envPtr)         /* Holds resulting instructions. */
02030 {
02031     JumpFixupArray jumpFalseFixupArray;
02032                                 /* Used to fix the ifFalse jump after each
02033                                  * test when its target PC is determined. */
02034     JumpFixupArray jumpEndFixupArray;
02035                                 /* Used to fix the jump after each "then" body
02036                                  * to the end of the "if" when that PC is
02037                                  * determined. */
02038     Tcl_Token *tokenPtr, *testTokenPtr;
02039     int jumpIndex = 0;          /* Avoid compiler warning. */
02040     int jumpFalseDist, numWords, wordIdx, numBytes, j, code;
02041     const char *word;
02042     int savedStackDepth = envPtr->currStackDepth;
02043                                 /* Saved stack depth at the start of the first
02044                                  * test; the envPtr current depth is restored
02045                                  * to this value at the start of each test. */
02046     int realCond = 1;           /* Set to 0 for static conditions:
02047                                  * "if 0 {..}" */
02048     int boolVal;                /* Value of static condition. */
02049     int compileScripts = 1;
02050     DefineLineInformation;      /* TIP #280 */
02051 
02052     /*
02053      * Only compile the "if" command if all arguments are simple words, in
02054      * order to insure correct substitution [Bug 219166]
02055      */
02056 
02057     tokenPtr = parsePtr->tokenPtr;
02058     wordIdx = 0;
02059     numWords = parsePtr->numWords;
02060 
02061     for (wordIdx = 0; wordIdx < numWords; wordIdx++) {
02062         if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
02063             return TCL_ERROR;
02064         }
02065         tokenPtr = TokenAfter(tokenPtr);
02066     }
02067 
02068     TclInitJumpFixupArray(&jumpFalseFixupArray);
02069     TclInitJumpFixupArray(&jumpEndFixupArray);
02070     code = TCL_OK;
02071 
02072     /*
02073      * Each iteration of this loop compiles one "if expr ?then? body" or
02074      * "elseif expr ?then? body" clause.
02075      */
02076 
02077     tokenPtr = parsePtr->tokenPtr;
02078     wordIdx = 0;
02079     while (wordIdx < numWords) {
02080         /*
02081          * Stop looping if the token isn't "if" or "elseif".
02082          */
02083 
02084         word = tokenPtr[1].start;
02085         numBytes = tokenPtr[1].size;
02086         if ((tokenPtr == parsePtr->tokenPtr)
02087                 || ((numBytes == 6) && (strncmp(word, "elseif", 6) == 0))) {
02088             tokenPtr = TokenAfter(tokenPtr);
02089             wordIdx++;
02090         } else {
02091             break;
02092         }
02093         if (wordIdx >= numWords) {
02094             code = TCL_ERROR;
02095             goto done;
02096         }
02097 
02098         /*
02099          * Compile the test expression then emit the conditional jump around
02100          * the "then" part.
02101          */
02102 
02103         envPtr->currStackDepth = savedStackDepth;
02104         testTokenPtr = tokenPtr;
02105 
02106         if (realCond) {
02107             /*
02108              * Find out if the condition is a constant.
02109              */
02110 
02111             Tcl_Obj *boolObj = Tcl_NewStringObj(testTokenPtr[1].start,
02112                     testTokenPtr[1].size);
02113             Tcl_IncrRefCount(boolObj);
02114             code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
02115             TclDecrRefCount(boolObj);
02116             if (code == TCL_OK) {
02117                 /*
02118                  * A static condition.
02119                  */
02120 
02121                 realCond = 0;
02122                 if (!boolVal) {
02123                     compileScripts = 0;
02124                 }
02125             } else {
02126                 envPtr->line = mapPtr->loc[eclIndex].line[wordIdx];
02127                 Tcl_ResetResult(interp);
02128                 TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
02129                 if (jumpFalseFixupArray.next >= jumpFalseFixupArray.end) {
02130                     TclExpandJumpFixupArray(&jumpFalseFixupArray);
02131                 }
02132                 jumpIndex = jumpFalseFixupArray.next;
02133                 jumpFalseFixupArray.next++;
02134                 TclEmitForwardJump(envPtr, TCL_FALSE_JUMP,
02135                         jumpFalseFixupArray.fixup+jumpIndex);
02136             }
02137             code = TCL_OK;
02138         }
02139 
02140         /*
02141          * Skip over the optional "then" before the then clause.
02142          */
02143 
02144         tokenPtr = TokenAfter(testTokenPtr);
02145         wordIdx++;
02146         if (wordIdx >= numWords) {
02147             code = TCL_ERROR;
02148             goto done;
02149         }
02150         if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
02151             word = tokenPtr[1].start;
02152             numBytes = tokenPtr[1].size;
02153             if ((numBytes == 4) && (strncmp(word, "then", 4) == 0)) {
02154                 tokenPtr = TokenAfter(tokenPtr);
02155                 wordIdx++;
02156                 if (wordIdx >= numWords) {
02157                     code = TCL_ERROR;
02158                     goto done;
02159                 }
02160             }
02161         }
02162 
02163         /*
02164          * Compile the "then" command body.
02165          */
02166 
02167         if (compileScripts) {
02168             envPtr->line = mapPtr->loc[eclIndex].line[wordIdx];
02169             envPtr->currStackDepth = savedStackDepth;
02170             CompileBody(envPtr, tokenPtr, interp);
02171         }
02172 
02173         if (realCond) {
02174             /*
02175              * Jump to the end of the "if" command. Both jumpFalseFixupArray
02176              * and jumpEndFixupArray are indexed by "jumpIndex".
02177              */
02178 
02179             if (jumpEndFixupArray.next >= jumpEndFixupArray.end) {
02180                 TclExpandJumpFixupArray(&jumpEndFixupArray);
02181             }
02182             jumpEndFixupArray.next++;
02183             TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
02184                     jumpEndFixupArray.fixup+jumpIndex);
02185 
02186             /*
02187              * Fix the target of the jumpFalse after the test. Generate a 4
02188              * byte jump if the distance is > 120 bytes. This is conservative,
02189              * and ensures that we won't have to replace this jump if we later
02190              * also need to replace the proceeding jump to the end of the "if"
02191              * with a 4 byte jump.
02192              */
02193 
02194             if (TclFixupForwardJumpToHere(envPtr,
02195                     jumpFalseFixupArray.fixup+jumpIndex, 120)) {
02196                 /*
02197                  * Adjust the code offset for the proceeding jump to the end
02198                  * of the "if" command.
02199                  */
02200 
02201                 jumpEndFixupArray.fixup[jumpIndex].codeOffset += 3;
02202             }
02203         } else if (boolVal) {
02204             /*
02205              * We were processing an "if 1 {...}"; stop compiling scripts.
02206              */
02207 
02208             compileScripts = 0;
02209         } else {
02210             /*
02211              * We were processing an "if 0 {...}"; reset so that the rest
02212              * (elseif, else) is compiled correctly.
02213              */
02214 
02215             realCond = 1;
02216             compileScripts = 1;
02217         }
02218 
02219         tokenPtr = TokenAfter(tokenPtr);
02220         wordIdx++;
02221     }
02222 
02223     /*
02224      * Restore the current stack depth in the environment; the "else" clause
02225      * (or its default) will add 1 to this.
02226      */
02227 
02228     envPtr->currStackDepth = savedStackDepth;
02229 
02230     /*
02231      * Check for the optional else clause. Do not compile anything if this was
02232      * an "if 1 {...}" case.
02233      */
02234 
02235     if ((wordIdx < numWords) && (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD)) {
02236         /*
02237          * There is an else clause. Skip over the optional "else" word.
02238          */
02239 
02240         word = tokenPtr[1].start;
02241         numBytes = tokenPtr[1].size;
02242         if ((numBytes == 4) && (strncmp(word, "else", 4) == 0)) {
02243             tokenPtr = TokenAfter(tokenPtr);
02244             wordIdx++;
02245             if (wordIdx >= numWords) {
02246                 code = TCL_ERROR;
02247                 goto done;
02248             }
02249         }
02250 
02251         if (compileScripts) {
02252             /*
02253              * Compile the else command body.
02254              */
02255 
02256             envPtr->line = mapPtr->loc[eclIndex].line[wordIdx];
02257             CompileBody(envPtr, tokenPtr, interp);
02258         }
02259 
02260         /*
02261          * Make sure there are no words after the else clause.
02262          */
02263 
02264         wordIdx++;
02265         if (wordIdx < numWords) {
02266             code = TCL_ERROR;
02267             goto done;
02268         }
02269     } else {
02270         /*
02271          * No else clause: the "if" command's result is an empty string.
02272          */
02273 
02274         if (compileScripts) {
02275             PushLiteral(envPtr, "", 0);
02276         }
02277     }
02278 
02279     /*
02280      * Fix the unconditional jumps to the end of the "if" command.
02281      */
02282 
02283     for (j = jumpEndFixupArray.next;  j > 0;  j--) {
02284         jumpIndex = (j - 1);    /* i.e. process the closest jump first. */
02285         if (TclFixupForwardJumpToHere(envPtr,
02286                 jumpEndFixupArray.fixup+jumpIndex, 127)) {
02287             /*
02288              * Adjust the immediately preceeding "ifFalse" jump. We moved it's
02289              * target (just after this jump) down three bytes.
02290              */
02291 
02292             unsigned char *ifFalsePc = envPtr->codeStart
02293                     + jumpFalseFixupArray.fixup[jumpIndex].codeOffset;
02294             unsigned char opCode = *ifFalsePc;
02295 
02296             if (opCode == INST_JUMP_FALSE1) {
02297                 jumpFalseDist = TclGetInt1AtPtr(ifFalsePc + 1);
02298                 jumpFalseDist += 3;
02299                 TclStoreInt1AtPtr(jumpFalseDist, (ifFalsePc + 1));
02300             } else if (opCode == INST_JUMP_FALSE4) {
02301                 jumpFalseDist = TclGetInt4AtPtr(ifFalsePc + 1);
02302                 jumpFalseDist += 3;
02303                 TclStoreInt4AtPtr(jumpFalseDist, (ifFalsePc + 1));
02304             } else {
02305                 Tcl_Panic("TclCompileIfCmd: unexpected opcode \"%d\" updating ifFalse jump", (int) opCode);
02306             }
02307         }
02308     }
02309 
02310     /*
02311      * Free the jumpFixupArray array if malloc'ed storage was used.
02312      */
02313 
02314   done:
02315     envPtr->currStackDepth = savedStackDepth + 1;
02316     TclFreeJumpFixupArray(&jumpFalseFixupArray);
02317     TclFreeJumpFixupArray(&jumpEndFixupArray);
02318     return code;
02319 }
02320 
02321 /*
02322  *----------------------------------------------------------------------
02323  *
02324  * TclCompileIncrCmd --
02325  *
02326  *      Procedure called to compile the "incr" command.
02327  *
02328  * Results:
02329  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02330  *      evaluation to runtime.
02331  *
02332  * Side effects:
02333  *      Instructions are added to envPtr to execute the "incr" command at
02334  *      runtime.
02335  *
02336  *----------------------------------------------------------------------
02337  */
02338 
02339 int
02340 TclCompileIncrCmd(
02341     Tcl_Interp *interp,         /* Used for error reporting. */
02342     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02343                                  * created by Tcl_ParseCommand. */
02344     Command *cmdPtr,            /* Points to defintion of command being
02345                                  * compiled. */
02346     CompileEnv *envPtr)         /* Holds resulting instructions. */
02347 {
02348     Tcl_Token *varTokenPtr, *incrTokenPtr;
02349     int simpleVarName, isScalar, localIndex, haveImmValue, immValue;
02350     DefineLineInformation;      /* TIP #280 */
02351 
02352     if ((parsePtr->numWords != 2) && (parsePtr->numWords != 3)) {
02353         return TCL_ERROR;
02354     }
02355 
02356     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
02357 
02358     PushVarName(interp, varTokenPtr, envPtr, TCL_NO_LARGE_INDEX|TCL_CREATE_VAR,
02359                 &localIndex, &simpleVarName, &isScalar,
02360                 mapPtr->loc[eclIndex].line[1]);
02361 
02362     /*
02363      * If an increment is given, push it, but see first if it's a small
02364      * integer.
02365      */
02366 
02367     haveImmValue = 0;
02368     immValue = 1;
02369     if (parsePtr->numWords == 3) {
02370         incrTokenPtr = TokenAfter(varTokenPtr);
02371         if (incrTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
02372             const char *word = incrTokenPtr[1].start;
02373             int numBytes = incrTokenPtr[1].size;
02374             int code;
02375             Tcl_Obj *intObj = Tcl_NewStringObj(word, numBytes);
02376             Tcl_IncrRefCount(intObj);
02377             code = TclGetIntFromObj(NULL, intObj, &immValue);
02378             TclDecrRefCount(intObj);
02379             if ((code == TCL_OK) && (-127 <= immValue) && (immValue <= 127)) {
02380                 haveImmValue = 1;
02381             }
02382             if (!haveImmValue) {
02383                 PushLiteral(envPtr, word, numBytes);
02384             }
02385         } else {
02386             envPtr->line = mapPtr->loc[eclIndex].line[2];
02387             CompileTokens(envPtr, incrTokenPtr, interp);
02388         }
02389     } else {                    /* No incr amount given so use 1. */
02390         haveImmValue = 1;
02391     }
02392 
02393     /*
02394      * Emit the instruction to increment the variable.
02395      */
02396 
02397     if (simpleVarName) {
02398         if (isScalar) {
02399             if (localIndex >= 0) {
02400                 if (haveImmValue) {
02401                     TclEmitInstInt1(INST_INCR_SCALAR1_IMM, localIndex, envPtr);
02402                     TclEmitInt1(immValue, envPtr);
02403                 } else {
02404                     TclEmitInstInt1(INST_INCR_SCALAR1, localIndex, envPtr);
02405                 }
02406             } else {
02407                 if (haveImmValue) {
02408                     TclEmitInstInt1(INST_INCR_SCALAR_STK_IMM, immValue, envPtr);
02409                 } else {
02410                     TclEmitOpcode(INST_INCR_SCALAR_STK, envPtr);
02411                 }
02412             }
02413         } else {
02414             if (localIndex >= 0) {
02415                 if (haveImmValue) {
02416                     TclEmitInstInt1(INST_INCR_ARRAY1_IMM, localIndex, envPtr);
02417                     TclEmitInt1(immValue, envPtr);
02418                 } else {
02419                     TclEmitInstInt1(INST_INCR_ARRAY1, localIndex, envPtr);
02420                 }
02421             } else {
02422                 if (haveImmValue) {
02423                     TclEmitInstInt1(INST_INCR_ARRAY_STK_IMM, immValue, envPtr);
02424                 } else {
02425                     TclEmitOpcode(INST_INCR_ARRAY_STK, envPtr);
02426                 }
02427             }
02428         }
02429     } else {                    /* Non-simple variable name. */
02430         if (haveImmValue) {
02431             TclEmitInstInt1(INST_INCR_STK_IMM, immValue, envPtr);
02432         } else {
02433             TclEmitOpcode(INST_INCR_STK, envPtr);
02434         }
02435     }
02436 
02437     return TCL_OK;
02438 }
02439 
02440 /*
02441  *----------------------------------------------------------------------
02442  *
02443  * TclCompileLappendCmd --
02444  *
02445  *      Procedure called to compile the "lappend" command.
02446  *
02447  * Results:
02448  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02449  *      evaluation to runtime.
02450  *
02451  * Side effects:
02452  *      Instructions are added to envPtr to execute the "lappend" command at
02453  *      runtime.
02454  *
02455  *----------------------------------------------------------------------
02456  */
02457 
02458 int
02459 TclCompileLappendCmd(
02460     Tcl_Interp *interp,         /* Used for error reporting. */
02461     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02462                                  * created by Tcl_ParseCommand. */
02463     Command *cmdPtr,            /* Points to defintion of command being
02464                                  * compiled. */
02465     CompileEnv *envPtr)         /* Holds resulting instructions. */
02466 {
02467     Tcl_Token *varTokenPtr;
02468     int simpleVarName, isScalar, localIndex, numWords;
02469     DefineLineInformation;      /* TIP #280 */
02470 
02471     /*
02472      * If we're not in a procedure, don't compile.
02473      */
02474 
02475     if (envPtr->procPtr == NULL) {
02476         return TCL_ERROR;
02477     }
02478 
02479     numWords = parsePtr->numWords;
02480     if (numWords == 1) {
02481         return TCL_ERROR;
02482     }
02483     if (numWords != 3) {
02484         /*
02485          * LAPPEND instructions currently only handle one value appends.
02486          */
02487 
02488         return TCL_ERROR;
02489     }
02490 
02491     /*
02492      * Decide if we can use a frame slot for the var/array name or if we
02493      * need to emit code to compute and push the name at runtime. We use a
02494      * frame slot (entry in the array of local vars) if we are compiling a
02495      * procedure body and if the name is simple text that does not include
02496      * namespace qualifiers.
02497      */
02498 
02499     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
02500 
02501     PushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
02502                 &localIndex, &simpleVarName, &isScalar,
02503                 mapPtr->loc[eclIndex].line[1]);
02504 
02505     /*
02506      * If we are doing an assignment, push the new value. In the no values
02507      * case, create an empty object.
02508      */
02509 
02510     if (numWords > 2) {
02511         Tcl_Token *valueTokenPtr = TokenAfter(varTokenPtr);
02512         CompileWord(envPtr, valueTokenPtr, interp, 2);
02513     }
02514 
02515     /*
02516      * Emit instructions to set/get the variable.
02517      */
02518 
02519     /*
02520      * The *_STK opcodes should be refactored to make better use of existing
02521      * LOAD/STORE instructions.
02522      */
02523 
02524     if (simpleVarName) {
02525         if (isScalar) {
02526             if (localIndex < 0) {
02527                 TclEmitOpcode(INST_LAPPEND_STK, envPtr);
02528             } else if (localIndex <= 255) {
02529                 TclEmitInstInt1(INST_LAPPEND_SCALAR1, localIndex, envPtr);
02530             } else {
02531                 TclEmitInstInt4(INST_LAPPEND_SCALAR4, localIndex, envPtr);
02532             }
02533         } else {
02534             if (localIndex < 0) {
02535                 TclEmitOpcode(INST_LAPPEND_ARRAY_STK, envPtr);
02536             } else if (localIndex <= 255) {
02537                 TclEmitInstInt1(INST_LAPPEND_ARRAY1, localIndex, envPtr);
02538             } else {
02539                 TclEmitInstInt4(INST_LAPPEND_ARRAY4, localIndex, envPtr);
02540             }
02541         }
02542     } else {
02543         TclEmitOpcode(INST_LAPPEND_STK, envPtr);
02544     }
02545 
02546     return TCL_OK;
02547 }
02548 
02549 /*
02550  *----------------------------------------------------------------------
02551  *
02552  * TclCompileLassignCmd --
02553  *
02554  *      Procedure called to compile the "lassign" command.
02555  *
02556  * Results:
02557  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02558  *      evaluation to runtime.
02559  *
02560  * Side effects:
02561  *      Instructions are added to envPtr to execute the "lassign" command at
02562  *      runtime.
02563  *
02564  *----------------------------------------------------------------------
02565  */
02566 
02567 int
02568 TclCompileLassignCmd(
02569     Tcl_Interp *interp,         /* Used for error reporting. */
02570     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02571                                  * created by Tcl_ParseCommand. */
02572     Command *cmdPtr,            /* Points to defintion of command being
02573                                  * compiled. */
02574     CompileEnv *envPtr)         /* Holds resulting instructions. */
02575 {
02576     Tcl_Token *tokenPtr;
02577     int simpleVarName, isScalar, localIndex, numWords, idx;
02578     DefineLineInformation;      /* TIP #280 */
02579 
02580     numWords = parsePtr->numWords;
02581 
02582     /*
02583      * Check for command syntax error, but we'll punt that to runtime.
02584      */
02585 
02586     if (numWords < 3) {
02587         return TCL_ERROR;
02588     }
02589 
02590     /*
02591      * Generate code to push list being taken apart by [lassign].
02592      */
02593 
02594     tokenPtr = TokenAfter(parsePtr->tokenPtr);
02595     CompileWord(envPtr, tokenPtr, interp, 1);
02596 
02597     /*
02598      * Generate code to assign values from the list to variables.
02599      */
02600 
02601     for (idx=0 ; idx<numWords-2 ; idx++) {
02602         tokenPtr = TokenAfter(tokenPtr);
02603 
02604         /*
02605          * Generate the next variable name.
02606          */
02607 
02608         PushVarName(interp, tokenPtr, envPtr, TCL_CREATE_VAR, &localIndex,
02609                 &simpleVarName, &isScalar, mapPtr->loc[eclIndex].line[idx+2]);
02610 
02611         /*
02612          * Emit instructions to get the idx'th item out of the list value on
02613          * the stack and assign it to the variable.
02614          */
02615 
02616         if (simpleVarName) {
02617             if (isScalar) {
02618                 if (localIndex >= 0) {
02619                     TclEmitOpcode(INST_DUP, envPtr);
02620                     TclEmitInstInt4(INST_LIST_INDEX_IMM, idx, envPtr);
02621                     if (localIndex <= 255) {
02622                         TclEmitInstInt1(INST_STORE_SCALAR1,localIndex,envPtr);
02623                     } else {
02624                         TclEmitInstInt4(INST_STORE_SCALAR4,localIndex,envPtr);
02625                     }
02626                 } else {
02627                     TclEmitInstInt4(INST_OVER, 1, envPtr);
02628                     TclEmitInstInt4(INST_LIST_INDEX_IMM, idx, envPtr);
02629                     TclEmitOpcode(INST_STORE_SCALAR_STK, envPtr);
02630                 }
02631             } else {
02632                 if (localIndex >= 0) {
02633                     TclEmitInstInt4(INST_OVER, 1, envPtr);
02634                     TclEmitInstInt4(INST_LIST_INDEX_IMM, idx, envPtr);
02635                     if (localIndex <= 255) {
02636                         TclEmitInstInt1(INST_STORE_ARRAY1, localIndex, envPtr);
02637                     } else {
02638                         TclEmitInstInt4(INST_STORE_ARRAY4, localIndex, envPtr);
02639                     }
02640                 } else {
02641                     TclEmitInstInt4(INST_OVER, 2, envPtr);
02642                     TclEmitInstInt4(INST_LIST_INDEX_IMM, idx, envPtr);
02643                     TclEmitOpcode(INST_STORE_ARRAY_STK, envPtr);
02644                 }
02645             }
02646         } else {
02647             TclEmitInstInt4(INST_OVER, 1, envPtr);
02648             TclEmitInstInt4(INST_LIST_INDEX_IMM, idx, envPtr);
02649             TclEmitOpcode(INST_STORE_STK, envPtr);
02650         }
02651         TclEmitOpcode(INST_POP, envPtr);
02652     }
02653 
02654     /*
02655      * Generate code to leave the rest of the list on the stack.
02656      */
02657 
02658     TclEmitInstInt4(INST_LIST_RANGE_IMM, idx, envPtr);
02659     TclEmitInt4(-2, envPtr);    /* -2 == "end" */
02660 
02661     return TCL_OK;
02662 }
02663 
02664 /*
02665  *----------------------------------------------------------------------
02666  *
02667  * TclCompileLindexCmd --
02668  *
02669  *      Procedure called to compile the "lindex" command.
02670  *
02671  * Results:
02672  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02673  *      evaluation to runtime.
02674  *
02675  * Side effects:
02676  *      Instructions are added to envPtr to execute the "lindex" command at
02677  *      runtime.
02678  *
02679  *----------------------------------------------------------------------
02680  */
02681 
02682 int
02683 TclCompileLindexCmd(
02684     Tcl_Interp *interp,         /* Used for error reporting. */
02685     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02686                                  * created by Tcl_ParseCommand. */
02687     Command *cmdPtr,            /* Points to defintion of command being
02688                                  * compiled. */
02689     CompileEnv *envPtr)         /* Holds resulting instructions. */
02690 {
02691     Tcl_Token *idxTokenPtr, *valTokenPtr;
02692     int i, numWords = parsePtr->numWords;
02693     DefineLineInformation;      /* TIP #280 */
02694 
02695     /*
02696      * Quit if too few args.
02697      */
02698 
02699     if (numWords <= 1) {
02700         return TCL_ERROR;
02701     }
02702 
02703     valTokenPtr = TokenAfter(parsePtr->tokenPtr);
02704     if (numWords != 3) {
02705         goto emitComplexLindex;
02706     }
02707 
02708     idxTokenPtr = TokenAfter(valTokenPtr);
02709     if (idxTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
02710         Tcl_Obj *tmpObj;
02711         int idx, result;
02712 
02713         tmpObj = Tcl_NewStringObj(idxTokenPtr[1].start, idxTokenPtr[1].size);
02714         result = TclGetIntFromObj(NULL, tmpObj, &idx);
02715         TclDecrRefCount(tmpObj);
02716 
02717         if (result == TCL_OK && idx >= 0) {
02718             /*
02719              * All checks have been completed, and we have exactly this
02720              * construct:
02721              *   lindex <arbitraryValue> <posInt>
02722              * This is best compiled as a push of the arbitrary value followed
02723              * by an "immediate lindex" which is the most efficient variety.
02724              */
02725 
02726             CompileWord(envPtr, valTokenPtr, interp, 1);
02727             TclEmitInstInt4(INST_LIST_INDEX_IMM, idx, envPtr);
02728             return TCL_OK;
02729         }
02730 
02731         /*
02732          * If the conversion failed or the value was negative, we just keep on
02733          * going with the more complex compilation.
02734          */
02735     }
02736 
02737     /*
02738      * Push the operands onto the stack.
02739      */
02740 
02741   emitComplexLindex:
02742     for (i=1 ; i<numWords ; i++) {
02743         CompileWord(envPtr, valTokenPtr, interp, i);
02744         valTokenPtr = TokenAfter(valTokenPtr);
02745     }
02746 
02747     /*
02748      * Emit INST_LIST_INDEX if objc==3, or INST_LIST_INDEX_MULTI if there are
02749      * multiple index args.
02750      */
02751 
02752     if (numWords == 3) {
02753         TclEmitOpcode(INST_LIST_INDEX, envPtr);
02754     } else {
02755         TclEmitInstInt4(INST_LIST_INDEX_MULTI, numWords-1, envPtr);
02756     }
02757 
02758     return TCL_OK;
02759 }
02760 
02761 /*
02762  *----------------------------------------------------------------------
02763  *
02764  * TclCompileListCmd --
02765  *
02766  *      Procedure called to compile the "list" command.
02767  *
02768  * Results:
02769  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02770  *      evaluation to runtime.
02771  *
02772  * Side effects:
02773  *      Instructions are added to envPtr to execute the "list" command at
02774  *      runtime.
02775  *
02776  *----------------------------------------------------------------------
02777  */
02778 
02779 int
02780 TclCompileListCmd(
02781     Tcl_Interp *interp,         /* Used for error reporting. */
02782     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02783                                  * created by Tcl_ParseCommand. */
02784     Command *cmdPtr,            /* Points to defintion of command being
02785                                  * compiled. */
02786     CompileEnv *envPtr)         /* Holds resulting instructions. */
02787 {
02788     DefineLineInformation;      /* TIP #280 */
02789 
02790     /*
02791      * If we're not in a procedure, don't compile.
02792      */
02793 
02794     if (envPtr->procPtr == NULL) {
02795         return TCL_ERROR;
02796     }
02797 
02798     if (parsePtr->numWords == 1) {
02799         /*
02800          * [list] without arguments just pushes an empty object.
02801          */
02802 
02803         PushLiteral(envPtr, "", 0);
02804     } else {
02805         /*
02806          * Push the all values onto the stack.
02807          */
02808 
02809         Tcl_Token *valueTokenPtr;
02810         int i, numWords;
02811 
02812         numWords = parsePtr->numWords;
02813 
02814         valueTokenPtr = TokenAfter(parsePtr->tokenPtr);
02815         for (i = 1; i < numWords; i++) {
02816             CompileWord(envPtr, valueTokenPtr, interp, i);
02817             valueTokenPtr = TokenAfter(valueTokenPtr);
02818         }
02819         TclEmitInstInt4(INST_LIST, numWords - 1, envPtr);
02820     }
02821 
02822     return TCL_OK;
02823 }
02824 
02825 /*
02826  *----------------------------------------------------------------------
02827  *
02828  * TclCompileLlengthCmd --
02829  *
02830  *      Procedure called to compile the "llength" command.
02831  *
02832  * Results:
02833  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02834  *      evaluation to runtime.
02835  *
02836  * Side effects:
02837  *      Instructions are added to envPtr to execute the "llength" command at
02838  *      runtime.
02839  *
02840  *----------------------------------------------------------------------
02841  */
02842 
02843 int
02844 TclCompileLlengthCmd(
02845     Tcl_Interp *interp,         /* Used for error reporting. */
02846     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
02847                                  * created by Tcl_ParseCommand. */
02848     Command *cmdPtr,            /* Points to defintion of command being
02849                                  * compiled. */
02850     CompileEnv *envPtr)         /* Holds resulting instructions. */
02851 {
02852     Tcl_Token *varTokenPtr;
02853     DefineLineInformation;      /* TIP #280 */
02854 
02855     if (parsePtr->numWords != 2) {
02856         return TCL_ERROR;
02857     }
02858     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
02859 
02860     CompileWord(envPtr, varTokenPtr, interp, 1);
02861     TclEmitOpcode(INST_LIST_LENGTH, envPtr);
02862     return TCL_OK;
02863 }
02864 
02865 /*
02866  *----------------------------------------------------------------------
02867  *
02868  * TclCompileLsetCmd --
02869  *
02870  *      Procedure called to compile the "lset" command.
02871  *
02872  * Results:
02873  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
02874  *      evaluation to runtime.
02875  *
02876  * Side effects:
02877  *      Instructions are added to envPtr to execute the "lset" command at
02878  *      runtime.
02879  *
02880  * The general template for execution of the "lset" command is:
02881  *      (1) Instructions to push the variable name, unless the variable is
02882  *          local to the stack frame.
02883  *      (2) If the variable is an array element, instructions to push the
02884  *          array element name.
02885  *      (3) Instructions to push each of zero or more "index" arguments to the
02886  *          stack, followed with the "newValue" element.
02887  *      (4) Instructions to duplicate the variable name and/or array element
02888  *          name onto the top of the stack, if either was pushed at steps (1)
02889  *          and (2).
02890  *      (5) The appropriate INST_LOAD_* instruction to place the original
02891  *          value of the list variable at top of stack.
02892  *      (6) At this point, the stack contains:
02893  *              varName? arrayElementName? index1 index2 ... newValue oldList
02894  *          The compiler emits one of INST_LSET_FLAT or INST_LSET_LIST
02895  *          according as whether there is exactly one index element (LIST) or
02896  *          either zero or else two or more (FLAT). This instruction removes
02897  *          everything from the stack except for the two names and pushes the
02898  *          new value of the variable.
02899  *      (7) Finally, INST_STORE_* stores the new value in the variable and
02900  *          cleans up the stack.
02901  *
02902  *----------------------------------------------------------------------
02903  */
02904 
02905 int
02906 TclCompileLsetCmd(
02907     Tcl_Interp *interp,         /* Tcl interpreter for error reporting. */
02908     Tcl_Parse *parsePtr,        /* Points to a parse structure for the
02909                                  * command. */
02910     Command *cmdPtr,            /* Points to defintion of command being
02911                                  * compiled. */
02912     CompileEnv *envPtr)         /* Holds the resulting instructions. */
02913 {
02914     int tempDepth;              /* Depth used for emitting one part of the
02915                                  * code burst. */
02916     Tcl_Token *varTokenPtr;     /* Pointer to the Tcl_Token representing the
02917                                  * parse of the variable name. */
02918     int localIndex;             /* Index of var in local var table. */
02919     int simpleVarName;          /* Flag == 1 if var name is simple. */
02920     int isScalar;               /* Flag == 1 if scalar, 0 if array. */
02921     int i;
02922     DefineLineInformation;      /* TIP #280 */
02923 
02924     /*
02925      * Check argument count.
02926      */
02927 
02928     if (parsePtr->numWords < 3) {
02929         /*
02930          * Fail at run time, not in compilation.
02931          */
02932 
02933         return TCL_ERROR;
02934     }
02935 
02936     /*
02937      * Decide if we can use a frame slot for the var/array name or if we need
02938      * to emit code to compute and push the name at runtime. We use a frame
02939      * slot (entry in the array of local vars) if we are compiling a procedure
02940      * body and if the name is simple text that does not include namespace
02941      * qualifiers.
02942      */
02943 
02944     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
02945     PushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
02946                 &localIndex, &simpleVarName, &isScalar,
02947                 mapPtr->loc[eclIndex].line[1]);
02948 
02949     /*
02950      * Push the "index" args and the new element value.
02951      */
02952 
02953     for (i=2 ; i<parsePtr->numWords ; ++i) {
02954         varTokenPtr = TokenAfter(varTokenPtr);
02955         CompileWord(envPtr, varTokenPtr, interp, i);
02956     }
02957 
02958     /*
02959      * Duplicate the variable name if it's been pushed.
02960      */
02961 
02962     if (!simpleVarName || localIndex < 0) {
02963         if (!simpleVarName || isScalar) {
02964             tempDepth = parsePtr->numWords - 2;
02965         } else {
02966             tempDepth = parsePtr->numWords - 1;
02967         }
02968         TclEmitInstInt4(INST_OVER, tempDepth, envPtr);
02969     }
02970 
02971     /*
02972      * Duplicate an array index if one's been pushed.
02973      */
02974 
02975     if (simpleVarName && !isScalar) {
02976         if (localIndex < 0) {
02977             tempDepth = parsePtr->numWords - 1;
02978         } else {
02979             tempDepth = parsePtr->numWords - 2;
02980         }
02981         TclEmitInstInt4(INST_OVER, tempDepth, envPtr);
02982     }
02983 
02984     /*
02985      * Emit code to load the variable's value.
02986      */
02987 
02988     if (!simpleVarName) {
02989         TclEmitOpcode(INST_LOAD_STK, envPtr);
02990     } else if (isScalar) {
02991         if (localIndex < 0) {
02992             TclEmitOpcode(INST_LOAD_SCALAR_STK, envPtr);
02993         } else if (localIndex < 0x100) {
02994             TclEmitInstInt1(INST_LOAD_SCALAR1, localIndex, envPtr);
02995         } else {
02996             TclEmitInstInt4(INST_LOAD_SCALAR4, localIndex, envPtr);
02997         }
02998     } else {
02999         if (localIndex < 0) {
03000             TclEmitOpcode(INST_LOAD_ARRAY_STK, envPtr);
03001         } else if (localIndex < 0x100) {
03002             TclEmitInstInt1(INST_LOAD_ARRAY1, localIndex, envPtr);
03003         } else {
03004             TclEmitInstInt4(INST_LOAD_ARRAY4, localIndex, envPtr);
03005         }
03006     }
03007 
03008     /*
03009      * Emit the correct variety of 'lset' instruction.
03010      */
03011 
03012     if (parsePtr->numWords == 4) {
03013         TclEmitOpcode(INST_LSET_LIST, envPtr);
03014     } else {
03015         TclEmitInstInt4(INST_LSET_FLAT, parsePtr->numWords-1, envPtr);
03016     }
03017 
03018     /*
03019      * Emit code to put the value back in the variable.
03020      */
03021 
03022     if (!simpleVarName) {
03023         TclEmitOpcode(INST_STORE_STK, envPtr);
03024     } else if (isScalar) {
03025         if (localIndex < 0) {
03026             TclEmitOpcode(INST_STORE_SCALAR_STK, envPtr);
03027         } else if (localIndex < 0x100) {
03028             TclEmitInstInt1(INST_STORE_SCALAR1, localIndex, envPtr);
03029         } else {
03030             TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr);
03031         }
03032     } else {
03033         if (localIndex < 0) {
03034             TclEmitOpcode(INST_STORE_ARRAY_STK, envPtr);
03035         } else if (localIndex < 0x100) {
03036             TclEmitInstInt1(INST_STORE_ARRAY1, localIndex, envPtr);
03037         } else {
03038             TclEmitInstInt4(INST_STORE_ARRAY4, localIndex, envPtr);
03039         }
03040     }
03041 
03042     return TCL_OK;
03043 }
03044 
03045 /*
03046  *----------------------------------------------------------------------
03047  *
03048  * TclCompileRegexpCmd --
03049  *
03050  *      Procedure called to compile the "regexp" command.
03051  *
03052  * Results:
03053  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03054  *      evaluation to runtime.
03055  *
03056  * Side effects:
03057  *      Instructions are added to envPtr to execute the "regexp" command at
03058  *      runtime.
03059  *
03060  *----------------------------------------------------------------------
03061  */
03062 
03063 int
03064 TclCompileRegexpCmd(
03065     Tcl_Interp *interp,         /* Tcl interpreter for error reporting. */
03066     Tcl_Parse *parsePtr,        /* Points to a parse structure for the
03067                                  * command. */
03068     Command *cmdPtr,            /* Points to defintion of command being
03069                                  * compiled. */
03070     CompileEnv *envPtr)         /* Holds the resulting instructions. */
03071 {
03072     Tcl_Token *varTokenPtr;     /* Pointer to the Tcl_Token representing the
03073                                  * parse of the RE or string. */
03074     int i, len, nocase, exact, sawLast, simple;
03075     char *str;
03076     DefineLineInformation;      /* TIP #280 */
03077 
03078     /*
03079      * We are only interested in compiling simple regexp cases. Currently
03080      * supported compile cases are:
03081      *   regexp ?-nocase? ?--? staticString $var
03082      *   regexp ?-nocase? ?--? {^staticString$} $var
03083      */
03084 
03085     if (parsePtr->numWords < 3) {
03086         return TCL_ERROR;
03087     }
03088 
03089     simple = 0;
03090     nocase = 0;
03091     sawLast = 0;
03092     varTokenPtr = parsePtr->tokenPtr;
03093 
03094     /*
03095      * We only look for -nocase and -- as options. Everything else gets pushed
03096      * to runtime execution. This is different than regexp's runtime option
03097      * handling, but satisfies our stricter needs.
03098      */
03099 
03100     for (i = 1; i < parsePtr->numWords - 2; i++) {
03101         varTokenPtr = TokenAfter(varTokenPtr);
03102         if (varTokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
03103             /*
03104              * Not a simple string, so punt to runtime.
03105              */
03106 
03107             return TCL_ERROR;
03108         }
03109         str = (char *) varTokenPtr[1].start;
03110         len = varTokenPtr[1].size;
03111         if ((len == 2) && (str[0] == '-') && (str[1] == '-')) {
03112             sawLast++;
03113             i++;
03114             break;
03115         } else if ((len > 1) && (strncmp(str,"-nocase",(unsigned)len) == 0)) {
03116             nocase = 1;
03117         } else {
03118             /*
03119              * Not an option we recognize.
03120              */
03121 
03122             return TCL_ERROR;
03123         }
03124     }
03125 
03126     if ((parsePtr->numWords - i) != 2) {
03127         /*
03128          * We don't support capturing to variables.
03129          */
03130 
03131         return TCL_ERROR;
03132     }
03133 
03134     /*
03135      * Get the regexp string. If it is not a simple string or can't be
03136      * converted to a glob pattern, push the word for the INST_REGEXP.
03137      * Keep changes here in sync with TclCompileSwitchCmd Switch_Regexp.
03138      */
03139 
03140     varTokenPtr = TokenAfter(varTokenPtr);
03141 
03142     if (varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
03143         Tcl_DString ds;
03144 
03145         str = (char *) varTokenPtr[1].start;
03146         len = varTokenPtr[1].size;
03147         /*
03148          * If it has a '-', it could be an incorrectly formed regexp command.
03149          */
03150 
03151         if ((*str == '-') && !sawLast) {
03152             return TCL_ERROR;
03153         }
03154 
03155         if (len == 0) {
03156             /*
03157              * The semantics of regexp are always match on re == "".
03158              */
03159 
03160             PushLiteral(envPtr, "1", 1);
03161             return TCL_OK;
03162         }
03163 
03164         /*
03165          * Attempt to convert pattern to glob.  If successful, push the
03166          * converted pattern as a literal.
03167          */
03168 
03169         if (TclReToGlob(NULL, varTokenPtr[1].start, len, &ds, &exact)
03170                 == TCL_OK) {
03171             simple = 1;
03172             PushLiteral(envPtr, Tcl_DStringValue(&ds),Tcl_DStringLength(&ds));
03173             Tcl_DStringFree(&ds);
03174         }
03175     }
03176 
03177     if (!simple) {
03178         CompileWord(envPtr, varTokenPtr, interp, parsePtr->numWords-2);
03179     }
03180 
03181     /*
03182      * Push the string arg.
03183      */
03184 
03185     varTokenPtr = TokenAfter(varTokenPtr);
03186     CompileWord(envPtr, varTokenPtr, interp, parsePtr->numWords-1);
03187 
03188     if (simple) {
03189         if (exact && !nocase) {
03190             TclEmitOpcode(INST_STR_EQ, envPtr);
03191         } else {
03192             TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
03193         }
03194     } else {
03195         /*
03196          * Pass correct RE compile flags.  We use only Int1 (8-bit), but
03197          * that handles all the flags we want to pass.
03198          * Don't use TCL_REG_NOSUB as we may have backrefs.
03199          */
03200         int cflags = TCL_REG_ADVANCED | (nocase ? TCL_REG_NOCASE : 0);
03201         TclEmitInstInt1(INST_REGEXP, cflags, envPtr);
03202     }
03203 
03204     return TCL_OK;
03205 }
03206 
03207 /*
03208  *----------------------------------------------------------------------
03209  *
03210  * TclCompileReturnCmd --
03211  *
03212  *      Procedure called to compile the "return" command.
03213  *
03214  * Results:
03215  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03216  *      evaluation to runtime.
03217  *
03218  * Side effects:
03219  *      Instructions are added to envPtr to execute the "return" command at
03220  *      runtime.
03221  *
03222  *----------------------------------------------------------------------
03223  */
03224 
03225 int
03226 TclCompileReturnCmd(
03227     Tcl_Interp *interp,         /* Used for error reporting. */
03228     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03229                                  * created by Tcl_ParseCommand. */
03230     Command *cmdPtr,            /* Points to defintion of command being
03231                                  * compiled. */
03232     CompileEnv *envPtr)         /* Holds resulting instructions. */
03233 {
03234     /*
03235      * General syntax: [return ?-option value ...? ?result?]
03236      * An even number of words means an explicit result argument is present.
03237      */
03238     int level, code, objc, size, status = TCL_OK;
03239     int numWords = parsePtr->numWords;
03240     int explicitResult = (0 == (numWords % 2));
03241     int numOptionWords = numWords - 1 - explicitResult;
03242     Tcl_Obj *returnOpts, **objv;
03243     Tcl_Token *wordTokenPtr = TokenAfter(parsePtr->tokenPtr);
03244     DefineLineInformation;      /* TIP #280 */
03245 
03246     /*
03247      * Check for special case which can always be compiled:
03248      *      return -options <opts> <msg>
03249      * Unlike the normal [return] compilation, this version does everything at
03250      * runtime so it can handle arbitrary words and not just literals. Note
03251      * that if INST_RETURN_STK wasn't already needed for something else
03252      * ('finally' clause processing) this piece of code would not be present.
03253      */
03254 
03255     if ((numWords == 4) && (wordTokenPtr->type == TCL_TOKEN_SIMPLE_WORD)
03256             && (wordTokenPtr[1].size == 8)
03257             && (strncmp(wordTokenPtr[1].start, "-options", 8) == 0)) {
03258         Tcl_Token *optsTokenPtr = TokenAfter(wordTokenPtr);
03259         Tcl_Token *msgTokenPtr = TokenAfter(optsTokenPtr);
03260 
03261         CompileWord(envPtr, optsTokenPtr, interp, 2);
03262         CompileWord(envPtr, msgTokenPtr,  interp, 3);
03263         TclEmitOpcode(INST_RETURN_STK, envPtr);
03264         return TCL_OK;
03265     }
03266 
03267     /*
03268      * Allocate some working space.
03269      */
03270 
03271     objv = (Tcl_Obj **) TclStackAlloc(interp,
03272             numOptionWords * sizeof(Tcl_Obj *));
03273 
03274     /*
03275      * Scan through the return options. If any are unknown at compile time,
03276      * there is no value in bytecompiling. Save the option values known in an
03277      * objv array for merging into a return options dictionary.
03278      */
03279 
03280     for (objc = 0; objc < numOptionWords; objc++) {
03281         objv[objc] = Tcl_NewObj();
03282         Tcl_IncrRefCount(objv[objc]);
03283         if (!TclWordKnownAtCompileTime(wordTokenPtr, objv[objc])) {
03284             objc++;
03285             status = TCL_ERROR;
03286             goto cleanup;
03287         }
03288         wordTokenPtr = TokenAfter(wordTokenPtr);
03289     }
03290     status = TclMergeReturnOptions(interp, objc, objv,
03291             &returnOpts, &code, &level);
03292   cleanup:
03293     while (--objc >= 0) {
03294         TclDecrRefCount(objv[objc]);
03295     }
03296     TclStackFree(interp, objv);
03297     if (TCL_ERROR == status) {
03298         /*
03299          * Something was bogus in the return options. Clear the error message,
03300          * and report back to the compiler that this must be interpreted at
03301          * runtime.
03302          */
03303 
03304         Tcl_ResetResult(interp);
03305         return TCL_ERROR;
03306     }
03307 
03308     /*
03309      * All options are known at compile time, so we're going to bytecompile.
03310      * Emit instructions to push the result on the stack.
03311      */
03312 
03313     if (explicitResult) {
03314          CompileWord(envPtr, wordTokenPtr, interp, numWords-1);
03315     } else {
03316         /*
03317          * No explict result argument, so default result is empty string.
03318          */
03319 
03320         PushLiteral(envPtr, "", 0);
03321     }
03322 
03323     /*
03324      * Check for optimization: When [return] is in a proc, and there's no
03325      * enclosing [catch], and there are no return options, then the INST_DONE
03326      * instruction is equivalent, and may be more efficient.
03327      */
03328 
03329     if (numOptionWords == 0 && envPtr->procPtr != NULL) {
03330         /*
03331          * We have default return options and we're in a proc ...
03332          */
03333 
03334         int index = envPtr->exceptArrayNext - 1;
03335         int enclosingCatch = 0;
03336 
03337         while (index >= 0) {
03338             ExceptionRange range = envPtr->exceptArrayPtr[index];
03339             if ((range.type == CATCH_EXCEPTION_RANGE)
03340                     && (range.catchOffset == -1)) {
03341                 enclosingCatch = 1;
03342                 break;
03343             }
03344             index--;
03345         }
03346         if (!enclosingCatch) {
03347             /*
03348              * ... and there is no enclosing catch. Issue the maximally
03349              * efficient exit instruction.
03350              */
03351 
03352             Tcl_DecrRefCount(returnOpts);
03353             TclEmitOpcode(INST_DONE, envPtr);
03354             return TCL_OK;
03355         }
03356     }
03357 
03358     /* Optimize [return -level 0 $x]. */
03359     Tcl_DictObjSize(NULL, returnOpts, &size);
03360     if (size == 0 && level == 0 && code == TCL_OK) {
03361         return TCL_OK;
03362     }
03363 
03364     /*
03365      * Could not use the optimization, so we push the return options dict, and
03366      * emit the INST_RETURN_IMM instruction with code and level as operands.
03367      */
03368 
03369     CompileReturnInternal(envPtr, INST_RETURN_IMM, code, level, returnOpts);
03370     return TCL_OK;
03371 }
03372 
03373 static void
03374 CompileReturnInternal(
03375     CompileEnv *envPtr,
03376     unsigned char op,
03377     int code,
03378     int level,
03379     Tcl_Obj *returnOpts)
03380 {
03381     TclEmitPush(TclAddLiteralObj(envPtr, returnOpts, NULL), envPtr);
03382     TclEmitInstInt4(op, code, envPtr);
03383     TclEmitInt4(level, envPtr);
03384 }
03385 
03386 void
03387 TclCompileSyntaxError(
03388     Tcl_Interp *interp,
03389     CompileEnv *envPtr)
03390 {
03391     Tcl_Obj *msg = Tcl_GetObjResult(interp);
03392     int numBytes;
03393     const char *bytes = TclGetStringFromObj(msg, &numBytes);
03394 
03395     TclEmitPush(TclRegisterNewLiteral(envPtr, bytes, numBytes), envPtr);
03396     CompileReturnInternal(envPtr, INST_SYNTAX, TCL_ERROR, 0,
03397             Tcl_GetReturnOptions(interp, TCL_ERROR));
03398 }
03399 
03400 /*
03401  *----------------------------------------------------------------------
03402  *
03403  * TclCompileSetCmd --
03404  *
03405  *      Procedure called to compile the "set" command.
03406  *
03407  * Results:
03408  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03409  *      evaluation to runtime.
03410  *
03411  * Side effects:
03412  *      Instructions are added to envPtr to execute the "set" command at
03413  *      runtime.
03414  *
03415  *----------------------------------------------------------------------
03416  */
03417 
03418 int
03419 TclCompileSetCmd(
03420     Tcl_Interp *interp,         /* Used for error reporting. */
03421     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03422                                  * created by Tcl_ParseCommand. */
03423     Command *cmdPtr,            /* Points to defintion of command being
03424                                  * compiled. */
03425     CompileEnv *envPtr)         /* Holds resulting instructions. */
03426 {
03427     Tcl_Token *varTokenPtr, *valueTokenPtr;
03428     int isAssignment, isScalar, simpleVarName, localIndex, numWords;
03429     DefineLineInformation;      /* TIP #280 */
03430 
03431     numWords = parsePtr->numWords;
03432     if ((numWords != 2) && (numWords != 3)) {
03433         return TCL_ERROR;
03434     }
03435     isAssignment = (numWords == 3);
03436 
03437     /*
03438      * Decide if we can use a frame slot for the var/array name or if we need
03439      * to emit code to compute and push the name at runtime. We use a frame
03440      * slot (entry in the array of local vars) if we are compiling a procedure
03441      * body and if the name is simple text that does not include namespace
03442      * qualifiers.
03443      */
03444 
03445     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
03446     PushVarName(interp, varTokenPtr, envPtr, TCL_CREATE_VAR,
03447                 &localIndex, &simpleVarName, &isScalar,
03448                 mapPtr->loc[eclIndex].line[1]);
03449 
03450     /*
03451      * If we are doing an assignment, push the new value.
03452      */
03453 
03454     if (isAssignment) {
03455         valueTokenPtr = TokenAfter(varTokenPtr);
03456         CompileWord(envPtr, valueTokenPtr, interp, 2);
03457     }
03458 
03459     /*
03460      * Emit instructions to set/get the variable.
03461      */
03462 
03463     if (simpleVarName) {
03464         if (isScalar) {
03465             if (localIndex < 0) {
03466                 TclEmitOpcode((isAssignment?
03467                         INST_STORE_SCALAR_STK : INST_LOAD_SCALAR_STK), envPtr);
03468             } else if (localIndex <= 255) {
03469                 TclEmitInstInt1((isAssignment?
03470                         INST_STORE_SCALAR1 : INST_LOAD_SCALAR1),
03471                         localIndex, envPtr);
03472             } else {
03473                 TclEmitInstInt4((isAssignment?
03474                         INST_STORE_SCALAR4 : INST_LOAD_SCALAR4),
03475                         localIndex, envPtr);
03476             }
03477         } else {
03478             if (localIndex < 0) {
03479                 TclEmitOpcode((isAssignment?
03480                         INST_STORE_ARRAY_STK : INST_LOAD_ARRAY_STK), envPtr);
03481             } else if (localIndex <= 255) {
03482                 TclEmitInstInt1((isAssignment?
03483                         INST_STORE_ARRAY1 : INST_LOAD_ARRAY1),
03484                         localIndex, envPtr);
03485             } else {
03486                 TclEmitInstInt4((isAssignment?
03487                         INST_STORE_ARRAY4 : INST_LOAD_ARRAY4),
03488                         localIndex, envPtr);
03489             }
03490         }
03491     } else {
03492         TclEmitOpcode((isAssignment? INST_STORE_STK : INST_LOAD_STK), envPtr);
03493     }
03494 
03495     return TCL_OK;
03496 }
03497 
03498 /*
03499  *----------------------------------------------------------------------
03500  *
03501  * TclCompileStringCmpCmd --
03502  *
03503  *      Procedure called to compile the simplest and most common form of the
03504  *      "string compare" command.
03505  *
03506  * Results:
03507  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03508  *      evaluation to runtime.
03509  *
03510  * Side effects:
03511  *      Instructions are added to envPtr to execute the "string compare"
03512  *      command at runtime.
03513  *
03514  *----------------------------------------------------------------------
03515  */
03516 
03517 int
03518 TclCompileStringCmpCmd(
03519     Tcl_Interp *interp,         /* Used for error reporting. */
03520     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03521                                  * created by Tcl_ParseCommand. */
03522     Command *cmdPtr,            /* Points to defintion of command being
03523                                  * compiled. */
03524     CompileEnv *envPtr)         /* Holds resulting instructions. */
03525 {
03526     DefineLineInformation;      /* TIP #280 */
03527     Tcl_Token *tokenPtr;
03528 
03529     /*
03530      * We don't support any flags; the bytecode isn't that sophisticated.
03531      */
03532 
03533     if (parsePtr->numWords != 3) {
03534         return TCL_ERROR;
03535     }
03536 
03537     /*
03538      * Push the two operands onto the stack and then the test.
03539      */
03540 
03541     tokenPtr = TokenAfter(parsePtr->tokenPtr);
03542     CompileWord(envPtr, tokenPtr, interp, 1);
03543     tokenPtr = TokenAfter(tokenPtr);
03544     CompileWord(envPtr, tokenPtr, interp, 2);
03545     TclEmitOpcode(INST_STR_CMP, envPtr);
03546     return TCL_OK;
03547 }
03548 
03549 /*
03550  *----------------------------------------------------------------------
03551  *
03552  * TclCompileStringEqualCmd --
03553  *
03554  *      Procedure called to compile the simplest and most common form of the
03555  *      "string equal" command.
03556  *
03557  * Results:
03558  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03559  *      evaluation to runtime.
03560  *
03561  * Side effects:
03562  *      Instructions are added to envPtr to execute the "string equal" command
03563  *      at runtime.
03564  *
03565  *----------------------------------------------------------------------
03566  */
03567 
03568 int
03569 TclCompileStringEqualCmd(
03570     Tcl_Interp *interp,         /* Used for error reporting. */
03571     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03572                                  * created by Tcl_ParseCommand. */
03573     Command *cmdPtr,            /* Points to defintion of command being
03574                                  * compiled. */
03575     CompileEnv *envPtr)         /* Holds resulting instructions. */
03576 {
03577     DefineLineInformation;      /* TIP #280 */
03578     Tcl_Token *tokenPtr;
03579 
03580     /*
03581      * We don't support any flags; the bytecode isn't that sophisticated.
03582      */
03583 
03584     if (parsePtr->numWords != 3) {
03585         return TCL_ERROR;
03586     }
03587 
03588     /*
03589      * Push the two operands onto the stack and then the test.
03590      */
03591 
03592     tokenPtr = TokenAfter(parsePtr->tokenPtr);
03593     CompileWord(envPtr, tokenPtr, interp, 1);
03594     tokenPtr = TokenAfter(tokenPtr);
03595     CompileWord(envPtr, tokenPtr, interp, 2);
03596     TclEmitOpcode(INST_STR_EQ, envPtr);
03597     return TCL_OK;
03598 }
03599 
03600 /*
03601  *----------------------------------------------------------------------
03602  *
03603  * TclCompileStringIndexCmd --
03604  *
03605  *      Procedure called to compile the simplest and most common form of the
03606  *      "string index" command.
03607  *
03608  * Results:
03609  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03610  *      evaluation to runtime.
03611  *
03612  * Side effects:
03613  *      Instructions are added to envPtr to execute the "string index" command
03614  *      at runtime.
03615  *
03616  *----------------------------------------------------------------------
03617  */
03618 
03619 int
03620 TclCompileStringIndexCmd(
03621     Tcl_Interp *interp,         /* Used for error reporting. */
03622     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03623                                  * created by Tcl_ParseCommand. */
03624     Command *cmdPtr,            /* Points to defintion of command being
03625                                  * compiled. */
03626     CompileEnv *envPtr)         /* Holds resulting instructions. */
03627 {
03628     DefineLineInformation;      /* TIP #280 */
03629     Tcl_Token *tokenPtr;
03630 
03631     if (parsePtr->numWords != 3) {
03632         return TCL_ERROR;
03633     }
03634 
03635     /*
03636      * Push the two operands onto the stack and then the index operation.
03637      */
03638 
03639     tokenPtr = TokenAfter(parsePtr->tokenPtr);
03640     CompileWord(envPtr, tokenPtr, interp, 1);
03641     tokenPtr = TokenAfter(tokenPtr);
03642     CompileWord(envPtr, tokenPtr, interp, 2);
03643     TclEmitOpcode(INST_STR_INDEX, envPtr);
03644     return TCL_OK;
03645 }
03646 
03647 /*
03648  *----------------------------------------------------------------------
03649  *
03650  * TclCompileStringMatchCmd --
03651  *
03652  *      Procedure called to compile the simplest and most common form of the
03653  *      "string match" command.
03654  *
03655  * Results:
03656  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03657  *      evaluation to runtime.
03658  *
03659  * Side effects:
03660  *      Instructions are added to envPtr to execute the "string match" command
03661  *      at runtime.
03662  *
03663  *----------------------------------------------------------------------
03664  */
03665 
03666 int
03667 TclCompileStringMatchCmd(
03668     Tcl_Interp *interp,         /* Used for error reporting. */
03669     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03670                                  * created by Tcl_ParseCommand. */
03671     Command *cmdPtr,            /* Points to defintion of command being
03672                                  * compiled. */
03673     CompileEnv *envPtr)         /* Holds resulting instructions. */
03674 {
03675     DefineLineInformation;      /* TIP #280 */
03676     Tcl_Token *tokenPtr;
03677     int i, length, exactMatch = 0, nocase = 0;
03678     const char *str;
03679 
03680     if (parsePtr->numWords < 3 || parsePtr->numWords > 4) {
03681         return TCL_ERROR;
03682     }
03683     tokenPtr = TokenAfter(parsePtr->tokenPtr);
03684 
03685     /*
03686      * Check if we have a -nocase flag.
03687      */
03688 
03689     if (parsePtr->numWords == 4) {
03690         if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
03691             return TCL_ERROR;
03692         }
03693         str = tokenPtr[1].start;
03694         length = tokenPtr[1].size;
03695         if ((length <= 1) || strncmp(str, "-nocase", (size_t) length)) {
03696             /*
03697              * Fail at run time, not in compilation.
03698              */
03699 
03700             return TCL_ERROR;
03701         }
03702         nocase = 1;
03703         tokenPtr = TokenAfter(tokenPtr);
03704     }
03705 
03706     /*
03707      * Push the strings to match against each other.
03708      */
03709 
03710     for (i = 0; i < 2; i++) {
03711         if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
03712             str = tokenPtr[1].start;
03713             length = tokenPtr[1].size;
03714             if (!nocase && (i == 0)) {
03715                 /*
03716                  * Trivial matches can be done by 'string equal'. If -nocase
03717                  * was specified, we can't do this because INST_STR_EQ has no
03718                  * support for nocase.
03719                  */
03720 
03721                 Tcl_Obj *copy = Tcl_NewStringObj(str, length);
03722 
03723                 Tcl_IncrRefCount(copy);
03724                 exactMatch = TclMatchIsTrivial(TclGetString(copy));
03725                 TclDecrRefCount(copy);
03726             }
03727             PushLiteral(envPtr, str, length);
03728         } else {
03729             envPtr->line = mapPtr->loc[eclIndex].line[i+1+nocase];
03730             CompileTokens(envPtr, tokenPtr, interp);
03731         }
03732         tokenPtr = TokenAfter(tokenPtr);
03733     }
03734 
03735     /*
03736      * Push the matcher.
03737      */
03738 
03739     if (exactMatch) {
03740         TclEmitOpcode(INST_STR_EQ, envPtr);
03741     } else {
03742         TclEmitInstInt1(INST_STR_MATCH, nocase, envPtr);
03743     }
03744     return TCL_OK;
03745 }
03746 
03747 /*
03748  *----------------------------------------------------------------------
03749  *
03750  * TclCompileStringLenCmd --
03751  *
03752  *      Procedure called to compile the simplest and most common form of the
03753  *      "string length" command.
03754  *
03755  * Results:
03756  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
03757  *      evaluation to runtime.
03758  *
03759  * Side effects:
03760  *      Instructions are added to envPtr to execute the "string length"
03761  *      command at runtime.
03762  *
03763  *----------------------------------------------------------------------
03764  */
03765 
03766 int
03767 TclCompileStringLenCmd(
03768     Tcl_Interp *interp,         /* Used for error reporting. */
03769     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03770                                  * created by Tcl_ParseCommand. */
03771     Command *cmdPtr,            /* Points to defintion of command being
03772                                  * compiled. */
03773     CompileEnv *envPtr)         /* Holds resulting instructions. */
03774 {
03775     DefineLineInformation;      /* TIP #280 */
03776     Tcl_Token *tokenPtr;
03777 
03778     if (parsePtr->numWords != 2) {
03779         return TCL_ERROR;
03780     }
03781 
03782     tokenPtr = TokenAfter(parsePtr->tokenPtr);
03783     if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) {
03784         /*
03785          * Here someone is asking for the length of a static string. Just push
03786          * the actual character (not byte) length.
03787          */
03788 
03789         char buf[TCL_INTEGER_SPACE];
03790         int len = Tcl_NumUtfChars(tokenPtr[1].start, tokenPtr[1].size);
03791 
03792         len = sprintf(buf, "%d", len);
03793         PushLiteral(envPtr, buf, len);
03794     } else {
03795         envPtr->line = mapPtr->loc[eclIndex].line[1];
03796         CompileTokens(envPtr, tokenPtr, interp);
03797         TclEmitOpcode(INST_STR_LEN, envPtr);
03798     }
03799     return TCL_OK;
03800 }
03801 
03802 /*
03803  *----------------------------------------------------------------------
03804  *
03805  * TclCompileSwitchCmd --
03806  *
03807  *      Procedure called to compile the "switch" command.
03808  *
03809  * Results:
03810  *      Returns TCL_OK for successful compile, or TCL_ERROR to defer
03811  *      evaluation to runtime (either when it is too complex to get the
03812  *      semantics right, or when we know for sure that it is an error but need
03813  *      the error to happen at the right time).
03814  *
03815  * Side effects:
03816  *      Instructions are added to envPtr to execute the "switch" command at
03817  *      runtime.
03818  *
03819  * FIXME:
03820  *      Stack depths are probably not calculated correctly.
03821  *
03822  *----------------------------------------------------------------------
03823  */
03824 
03825 int
03826 TclCompileSwitchCmd(
03827     Tcl_Interp *interp,         /* Used for error reporting. */
03828     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
03829                                  * created by Tcl_ParseCommand. */
03830     Command *cmdPtr,            /* Points to defintion of command being
03831                                  * compiled. */
03832     CompileEnv *envPtr)         /* Holds resulting instructions. */
03833 {
03834     Tcl_Token *tokenPtr;        /* Pointer to tokens in command. */
03835     int numWords;               /* Number of words in command. */
03836 
03837     Tcl_Token *valueTokenPtr;   /* Token for the value to switch on. */
03838     enum {Switch_Exact, Switch_Glob, Switch_Regexp} mode;
03839                                 /* What kind of switch are we doing? */
03840 
03841     Tcl_Token *bodyTokenArray;  /* Array of real pattern list items. */
03842     Tcl_Token **bodyToken;      /* Array of pointers to pattern list items. */
03843     int *bodyLines;             /* Array of line numbers for body list
03844                                  * items. */
03845     int foundDefault;           /* Flag to indicate whether a "default" clause
03846                                  * is present. */
03847 
03848     JumpFixup *fixupArray;      /* Array of forward-jump fixup records. */
03849     int *fixupTargetArray;      /* Array of places for fixups to point at. */
03850     int fixupCount;             /* Number of places to fix up. */
03851     int contFixIndex;           /* Where the first of the jumps due to a group
03852                                  * of continuation bodies starts, or -1 if
03853                                  * there aren't any. */
03854     int contFixCount;           /* Number of continuation bodies pointing to
03855                                  * the current (or next) real body. */
03856 
03857     int savedStackDepth = envPtr->currStackDepth;
03858     int noCase;                 /* Has the -nocase flag been given? */
03859     int foundMode = 0;          /* Have we seen a mode flag yet? */
03860     int isListedArms = 0;
03861     int i, valueIndex;
03862     DefineLineInformation;      /* TIP #280 */
03863 
03864     /*
03865      * Only handle the following versions:
03866      *   switch         ?--? word {pattern body ...}
03867      *   switch -exact  ?--? word {pattern body ...}
03868      *   switch -glob   ?--? word {pattern body ...}
03869      *   switch -regexp ?--? word {pattern body ...}
03870      *   switch         --   word simpleWordPattern simpleWordBody ...
03871      *   switch -exact  --   word simpleWordPattern simpleWordBody ...
03872      *   switch -glob   --   word simpleWordPattern simpleWordBody ...
03873      *   switch -regexp --   word simpleWordPattern simpleWordBody ...
03874      * When the mode is -glob, can also handle a -nocase flag.
03875      *
03876      * First off, we don't care how the command's word was generated; we're
03877      * compiling it anyway! So skip it...
03878      */
03879 
03880     tokenPtr = TokenAfter(parsePtr->tokenPtr);
03881     valueIndex = 1;
03882     numWords = parsePtr->numWords-1;
03883 
03884     /*
03885      * Check for options.
03886      */
03887 
03888     noCase = 0;
03889     mode = Switch_Exact;
03890     if (numWords == 2) {
03891         /*
03892          * There's just the switch value and the bodies list. In that case, we
03893          * can skip all option parsing and move on to consider switch values
03894          * and the body list.
03895          */
03896 
03897         goto finishedOptionParse;
03898     }
03899 
03900     /*
03901      * There must be at least one option, --, because without that there is no
03902      * way to statically avoid the problems you get from strings-to-be-matched
03903      * that start with a - (the interpreted code falls apart if it encounters
03904      * them, so we punt if we *might* encounter them as that is the easiest
03905      * way of emulating the behaviour).
03906      */
03907 
03908     for (; numWords>=3 ; tokenPtr=TokenAfter(tokenPtr),numWords--) {
03909         register unsigned size = tokenPtr[1].size;
03910         register const char *chrs = tokenPtr[1].start;
03911 
03912         /*
03913          * We only process literal options, and we assume that -e, -g and -n
03914          * are unique prefixes of -exact, -glob and -nocase respectively (true
03915          * at time of writing). Note that -exact and -glob may only be given
03916          * at most once or we bail out (error case).
03917          */
03918 
03919         if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD || size < 2) {
03920             return TCL_ERROR;
03921         }
03922 
03923         if ((size <= 6) && !memcmp(chrs, "-exact", size)) {
03924             if (foundMode) {
03925                 return TCL_ERROR;
03926             }
03927             mode = Switch_Exact;
03928             foundMode = 1;
03929             valueIndex++;
03930             continue;
03931         } else if ((size <= 5) && !memcmp(chrs, "-glob", size)) {
03932             if (foundMode) {
03933                 return TCL_ERROR;
03934             }
03935             mode = Switch_Glob;
03936             foundMode = 1;
03937             valueIndex++;
03938             continue;
03939         } else if ((size <= 7) && !memcmp(chrs, "-regexp", size)) {
03940             if (foundMode) {
03941                 return TCL_ERROR;
03942             }
03943             mode = Switch_Regexp;
03944             foundMode = 1;
03945             valueIndex++;
03946             continue;
03947         } else if ((size <= 7) && !memcmp(chrs, "-nocase", size)) {
03948             noCase = 1;
03949             valueIndex++;
03950             continue;
03951         } else if ((size == 2) && !memcmp(chrs, "--", 2)) {
03952             valueIndex++;
03953             break;
03954         }
03955 
03956         /*
03957          * The switch command has many flags we cannot compile at all (e.g.
03958          * all the RE-related ones) which we must have encountered. Either
03959          * that or we have run off the end. The action here is the same: punt
03960          * to interpreted version.
03961          */
03962 
03963         return TCL_ERROR;
03964     }
03965     if (numWords < 3) {
03966         return TCL_ERROR;
03967     }
03968     tokenPtr = TokenAfter(tokenPtr);
03969     numWords--;
03970     if (noCase && (mode != Switch_Exact)) {
03971         /*
03972          * Can't compile this case; no opcode for case-insensitive equality!
03973          */
03974 
03975         return TCL_ERROR;
03976     }
03977 
03978     /*
03979      * The value to test against is going to always get pushed on the stack.
03980      * But not yet; we need to verify that the rest of the command is
03981      * compilable too.
03982      */
03983 
03984   finishedOptionParse:
03985     valueTokenPtr = tokenPtr;
03986     /* For valueIndex, see previous loop. */
03987     tokenPtr = TokenAfter(tokenPtr);
03988     numWords--;
03989 
03990     /*
03991      * Build an array of tokens for the matcher terms and script bodies. Note
03992      * that in the case of the quoted bodies, this is tricky as we cannot use
03993      * copies of the string from the input token for the generated tokens (it
03994      * causes a crash during exception handling). When multiple tokens are
03995      * available at this point, this is pretty easy.
03996      */
03997 
03998     if (numWords == 1) {
03999         Tcl_DString bodyList;
04000         const char **argv = NULL, *tokenStartPtr, *p;
04001         int bline;              /* TIP #280: line of the pattern/action list,
04002                                  * and start of list for when tracking the
04003                                  * location. This list comes immediately after
04004                                  * the value we switch on. */
04005         int isTokenBraced;
04006 
04007         /*
04008          * Test that we've got a suitable body list as a simple (i.e. braced)
04009          * word, and that the elements of the body are simple words too. This
04010          * is really rather nasty indeed.
04011          */
04012 
04013         if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
04014             return TCL_ERROR;
04015         }
04016 
04017         Tcl_DStringInit(&bodyList);
04018         Tcl_DStringAppend(&bodyList, tokenPtr[1].start, tokenPtr[1].size);
04019         if (Tcl_SplitList(NULL, Tcl_DStringValue(&bodyList), &numWords,
04020                 &argv) != TCL_OK) {
04021             Tcl_DStringFree(&bodyList);
04022             return TCL_ERROR;
04023         }
04024         Tcl_DStringFree(&bodyList);
04025 
04026         /*
04027          * Now we know what the switch arms are, we've got to see whether we
04028          * can synthesize tokens for the arms. First check whether we've got a
04029          * valid number of arms since we can do that now.
04030          */
04031 
04032         if (numWords == 0 || numWords % 2) {
04033             ckfree((char *) argv);
04034             return TCL_ERROR;
04035         }
04036 
04037         isListedArms = 1;
04038         bodyTokenArray = (Tcl_Token *) ckalloc(sizeof(Tcl_Token) * numWords);
04039         bodyToken = (Tcl_Token **) ckalloc(sizeof(Tcl_Token *) * numWords);
04040         bodyLines = (int *) ckalloc(sizeof(int) * numWords);
04041 
04042         /*
04043          * Locate the start of the arms within the overall word.
04044          */
04045 
04046         bline = mapPtr->loc[eclIndex].line[valueIndex+1];
04047         p = tokenStartPtr = tokenPtr[1].start;
04048         while (isspace(UCHAR(*tokenStartPtr))) {
04049             tokenStartPtr++;
04050         }
04051         if (*tokenStartPtr == '{') {
04052             tokenStartPtr++;
04053             isTokenBraced = 1;
04054         } else {
04055             isTokenBraced = 0;
04056         }
04057 
04058         /*
04059          * TIP #280: Count lines within the literal list.
04060          */
04061 
04062         for (i=0 ; i<numWords ; i++) {
04063             bodyTokenArray[i].type = TCL_TOKEN_TEXT;
04064             bodyTokenArray[i].start = tokenStartPtr;
04065             bodyTokenArray[i].size = strlen(argv[i]);
04066             bodyTokenArray[i].numComponents = 0;
04067             bodyToken[i] = bodyTokenArray+i;
04068             tokenStartPtr += bodyTokenArray[i].size;
04069 
04070             /*
04071              * Test to see if we have guessed the end of the word correctly;
04072              * if not, we can't feed the real string to the sub-compilation
04073              * engine, and we're then stuck and so have to punt out to doing
04074              * everything at runtime.
04075              */
04076 
04077             if ((isTokenBraced && *(tokenStartPtr++) != '}') ||
04078                     (tokenStartPtr < tokenPtr[1].start+tokenPtr[1].size
04079                     && !isspace(UCHAR(*tokenStartPtr)))) {
04080                 ckfree((char *) argv);
04081                 ckfree((char *) bodyToken);
04082                 ckfree((char *) bodyTokenArray);
04083                 ckfree((char *) bodyLines);
04084                 return TCL_ERROR;
04085             }
04086 
04087             /*
04088              * TIP #280: Now determine the line the list element starts on
04089              * (there is no need to do it earlier, due to the possibility of
04090              * aborting, see above).
04091              */
04092 
04093             TclAdvanceLines(&bline, p, bodyTokenArray[i].start);
04094             bodyLines[i] = bline;
04095             p = bodyTokenArray[i].start;
04096 
04097             while (isspace(UCHAR(*tokenStartPtr))) {
04098                 tokenStartPtr++;
04099                 if (tokenStartPtr >= tokenPtr[1].start+tokenPtr[1].size) {
04100                     break;
04101                 }
04102             }
04103             if (*tokenStartPtr == '{') {
04104                 tokenStartPtr++;
04105                 isTokenBraced = 1;
04106             } else {
04107                 isTokenBraced = 0;
04108             }
04109         }
04110         ckfree((char *) argv);
04111 
04112         /*
04113          * Check that we've parsed everything we thought we were going to
04114          * parse. If not, something odd is going on (I believe it is possible
04115          * to defeat the code above) and we should bail out.
04116          */
04117 
04118         if (tokenStartPtr != tokenPtr[1].start+tokenPtr[1].size) {
04119             ckfree((char *) bodyToken);
04120             ckfree((char *) bodyTokenArray);
04121             ckfree((char *) bodyLines);
04122             return TCL_ERROR;
04123         }
04124 
04125     } else if (numWords % 2 || numWords == 0) {
04126         /*
04127          * Odd number of words (>1) available, or no words at all available.
04128          * Both are error cases, so punt and let the interpreted-version
04129          * generate the error message. Note that the second case probably
04130          * should get caught earlier, but it's easy to check here again anyway
04131          * because it'd cause a nasty crash otherwise.
04132          */
04133 
04134         return TCL_ERROR;
04135     } else {
04136         /*
04137          * Multi-word definition of patterns & actions.
04138          */
04139 
04140         bodyToken = (Tcl_Token **) ckalloc(sizeof(Tcl_Token *) * numWords);
04141         bodyLines = (int *) ckalloc(sizeof(int) * numWords);
04142         bodyTokenArray = NULL;
04143         for (i=0 ; i<numWords ; i++) {
04144             /*
04145              * We only handle the very simplest case. Anything more complex is
04146              * a good reason to go to the interpreted case anyway due to
04147              * traces, etc.
04148              */
04149 
04150             if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD ||
04151                     tokenPtr->numComponents != 1) {
04152                 ckfree((char *) bodyToken);
04153                 ckfree((char *) bodyLines);
04154                 return TCL_ERROR;
04155             }
04156             bodyToken[i] = tokenPtr+1;
04157 
04158             /*
04159              * TIP #280: Copy line information from regular cmd info.
04160              */
04161 
04162             bodyLines[i] = mapPtr->loc[eclIndex].line[valueIndex+1+i];
04163             tokenPtr = TokenAfter(tokenPtr);
04164         }
04165     }
04166 
04167     /*
04168      * Fall back to interpreted if the last body is a continuation (it's
04169      * illegal, but this makes the error happen at the right time).
04170      */
04171 
04172     if (bodyToken[numWords-1]->size == 1 &&
04173             bodyToken[numWords-1]->start[0] == '-') {
04174         ckfree((char *) bodyToken);
04175         ckfree((char *) bodyLines);
04176         if (bodyTokenArray != NULL) {
04177             ckfree((char *) bodyTokenArray);
04178         }
04179         return TCL_ERROR;
04180     }
04181 
04182     /*
04183      * Now we commit to generating code; the parsing stage per se is done.
04184      * First, we push the value we're matching against on the stack.
04185      */
04186 
04187     envPtr->line = mapPtr->loc[eclIndex].line[valueIndex];
04188     CompileTokens(envPtr, valueTokenPtr, interp);
04189 
04190     /*
04191      * Check if we can generate a jump table, since if so that's faster than
04192      * doing an explicit compare with each body. Note that we're definitely
04193      * over-conservative with determining whether we can do the jump table,
04194      * but it handles the most common case well enough.
04195      */
04196 
04197     if (isListedArms && mode == Switch_Exact && !noCase) {
04198         JumptableInfo *jtPtr;
04199         int infoIndex, isNew, *finalFixups, numRealBodies = 0, jumpLocation;
04200         int mustGenerate, jumpToDefault;
04201         Tcl_DString buffer;
04202         Tcl_HashEntry *hPtr;
04203 
04204         /*
04205          * Compile the switch by using a jump table, which is basically a
04206          * hashtable that maps from literal values to match against to the
04207          * offset (relative to the INST_JUMP_TABLE instruction) to jump to.
04208          * The jump table itself is independent of any invokation of the
04209          * bytecode, and as such is stored in an auxData block.
04210          *
04211          * Start by allocating the jump table itself, plus some workspace.
04212          */
04213 
04214         jtPtr = (JumptableInfo *) ckalloc(sizeof(JumptableInfo));
04215         Tcl_InitHashTable(&jtPtr->hashTable, TCL_STRING_KEYS);
04216         infoIndex = TclCreateAuxData(jtPtr, &tclJumptableInfoType, envPtr);
04217         finalFixups = (int *) ckalloc(sizeof(int) * (numWords/2));
04218         foundDefault = 0;
04219         mustGenerate = 1;
04220 
04221         /*
04222          * Next, issue the instruction to do the jump, together with what we
04223          * want to do if things do not work out (jump to either the default
04224          * clause or the "default" default, which just sets the result to
04225          * empty). Note that we will come back and rewrite the jump's offset
04226          * parameter when we know what it should be, and that all jumps we
04227          * issue are of the wide kind because that makes the code much easier
04228          * to debug!
04229          */
04230 
04231         jumpLocation = CurrentOffset(envPtr);
04232         TclEmitInstInt4(INST_JUMP_TABLE, infoIndex, envPtr);
04233         jumpToDefault = CurrentOffset(envPtr);
04234         TclEmitInstInt4(INST_JUMP4, 0, envPtr);
04235 
04236         for (i=0 ; i<numWords ; i+=2) {
04237             /*
04238              * For each arm, we must first work out what to do with the match
04239              * term.
04240              */
04241 
04242             if (i!=numWords-2 || bodyToken[numWords-2]->size != 7 ||
04243                     memcmp(bodyToken[numWords-2]->start, "default", 7)) {
04244                 /*
04245                  * This is not a default clause, so insert the current
04246                  * location as a target in the jump table (assuming it isn't
04247                  * already there, which would indicate that this clause is
04248                  * probably masked by an earlier one). Note that we use a
04249                  * Tcl_DString here simply because the hash API does not let
04250                  * us specify the string length.
04251                  */
04252 
04253                 Tcl_DStringInit(&buffer);
04254                 Tcl_DStringAppend(&buffer, bodyToken[i]->start,
04255                         bodyToken[i]->size);
04256                 hPtr = Tcl_CreateHashEntry(&jtPtr->hashTable,
04257                         Tcl_DStringValue(&buffer), &isNew);
04258                 if (isNew) {
04259                     /*
04260                      * First time we've encountered this match clause, so it
04261                      * must point to here.
04262                      */
04263 
04264                     Tcl_SetHashValue(hPtr, (ClientData)
04265                             (CurrentOffset(envPtr) - jumpLocation));
04266                 }
04267                 Tcl_DStringFree(&buffer);
04268             } else {
04269                 /*
04270                  * This is a default clause, so patch up the fallthrough from
04271                  * the INST_JUMP_TABLE instruction to here.
04272                  */
04273 
04274                 foundDefault = 1;
04275                 isNew = 1;
04276                 TclStoreInt4AtPtr(CurrentOffset(envPtr)-jumpToDefault,
04277                         envPtr->codeStart+jumpToDefault+1);
04278             }
04279 
04280             /*
04281              * Now, for each arm we must deal with the body of the clause.
04282              *
04283              * If this is a continuation body (never true of a final clause,
04284              * whether default or not) we're done because the next jump target
04285              * will also point here, so we advance to the next clause.
04286              */
04287 
04288             if (bodyToken[i+1]->size == 1 && bodyToken[i+1]->start[0] == '-') {
04289                 mustGenerate = 1;
04290                 continue;
04291             }
04292 
04293             /*
04294              * Also skip this arm if its only match clause is masked. (We
04295              * could probably be more aggressive about this, but that would be
04296              * much more difficult to get right.)
04297              */
04298 
04299             if (!isNew && !mustGenerate) {
04300                 continue;
04301             }
04302             mustGenerate = 0;
04303 
04304             /*
04305              * Compile the body of the arm.
04306              */
04307 
04308             envPtr->line = bodyLines[i+1];      /* TIP #280 */
04309             TclCompileCmdWord(interp, bodyToken[i+1], 1, envPtr);
04310 
04311             /*
04312              * Compile a jump in to the end of the command if this body is
04313              * anything other than a user-supplied default arm (to either skip
04314              * over the remaining bodies or the code that generates an empty
04315              * result).
04316              */
04317 
04318             if (i+2 < numWords || !foundDefault) {
04319                 finalFixups[numRealBodies++] = CurrentOffset(envPtr);
04320 
04321                 /*
04322                  * Easier by far to issue this jump as a fixed-width jump.
04323                  * Otherwise we'd need to do a lot more (and more awkward)
04324                  * rewriting when we fixed this all up.
04325                  */
04326 
04327                 TclEmitInstInt4(INST_JUMP4, 0, envPtr);
04328             }
04329         }
04330 
04331         /*
04332          * We're at the end. If we've not already done so through the
04333          * processing of a user-supplied default clause, add in a "default"
04334          * default clause now.
04335          */
04336 
04337         if (!foundDefault) {
04338             TclStoreInt4AtPtr(CurrentOffset(envPtr)-jumpToDefault,
04339                     envPtr->codeStart+jumpToDefault+1);
04340             PushLiteral(envPtr, "", 0);
04341         }
04342 
04343         /*
04344          * No more instructions to be issued; everything that needs to jump to
04345          * the end of the command is fixed up at this point.
04346          */
04347 
04348         for (i=0 ; i<numRealBodies ; i++) {
04349             TclStoreInt4AtPtr(CurrentOffset(envPtr)-finalFixups[i],
04350                     envPtr->codeStart+finalFixups[i]+1);
04351         }
04352 
04353         /*
04354          * Clean up all our temporary space and return.
04355          */
04356 
04357         ckfree((char *) finalFixups);
04358         ckfree((char *) bodyToken);
04359         ckfree((char *) bodyLines);
04360         if (bodyTokenArray != NULL) {
04361             ckfree((char *) bodyTokenArray);
04362         }
04363         return TCL_OK;
04364     }
04365 
04366     /*
04367      * Generate a test for each arm.
04368      */
04369 
04370     contFixIndex = -1;
04371     contFixCount = 0;
04372     fixupArray = (JumpFixup *) ckalloc(sizeof(JumpFixup) * numWords);
04373     fixupTargetArray = (int *) ckalloc(sizeof(int) * numWords);
04374     memset(fixupTargetArray, 0, numWords * sizeof(int));
04375     fixupCount = 0;
04376     foundDefault = 0;
04377     for (i=0 ; i<numWords ; i+=2) {
04378         int nextArmFixupIndex = -1;
04379         envPtr->currStackDepth = savedStackDepth + 1;
04380         if (i!=numWords-2 || bodyToken[numWords-2]->size != 7 ||
04381                 memcmp(bodyToken[numWords-2]->start, "default", 7)) {
04382             /*
04383              * Generate the test for the arm.
04384              */
04385 
04386             switch (mode) {
04387             case Switch_Exact:
04388                 TclEmitOpcode(INST_DUP, envPtr);
04389                 TclCompileTokens(interp, bodyToken[i], 1, envPtr);
04390                 TclEmitOpcode(INST_STR_EQ, envPtr);
04391                 break;
04392             case Switch_Glob:
04393                 TclCompileTokens(interp, bodyToken[i], 1, envPtr);
04394                 TclEmitInstInt4(INST_OVER, 1, envPtr);
04395                 TclEmitInstInt1(INST_STR_MATCH, noCase, envPtr);
04396                 break;
04397             case Switch_Regexp: {
04398                 int simple = 0, exact = 0;
04399 
04400                 /*
04401                  * Keep in sync with TclCompileRegexpCmd.
04402                  */
04403                 if (bodyToken[i]->type == TCL_TOKEN_TEXT) {
04404                     Tcl_DString ds;
04405 
04406                     if (bodyToken[i]->size == 0) {
04407                         /*
04408                          * The semantics of regexps are that they always match
04409                          * when the RE == "".
04410                          */
04411 
04412                         PushLiteral(envPtr, "1", 1);
04413                         break;
04414                     }
04415 
04416                     /*
04417                      * Attempt to convert pattern to glob. If successful, push
04418                      * the converted pattern.
04419                      */
04420 
04421                     if (TclReToGlob(NULL, bodyToken[i]->start,
04422                             bodyToken[i]->size, &ds, &exact) == TCL_OK) {
04423                         simple = 1;
04424                         PushLiteral(envPtr, Tcl_DStringValue(&ds),
04425                                 Tcl_DStringLength(&ds));
04426                         Tcl_DStringFree(&ds);
04427                     }
04428                 }
04429                 if (!simple) {
04430                     TclCompileTokens(interp, bodyToken[i], 1, envPtr);
04431                 }
04432 
04433                 TclEmitInstInt4(INST_OVER, 1, envPtr);
04434                 if (simple) {
04435                     if (exact && !noCase) {
04436                         TclEmitOpcode(INST_STR_EQ, envPtr);
04437                     } else {
04438                         TclEmitInstInt1(INST_STR_MATCH, noCase, envPtr);
04439                     }
04440                 } else {
04441                     /*
04442                      * Pass correct RE compile flags.  We use only Int1
04443                      * (8-bit), but that handles all the flags we want to
04444                      * pass.  Don't use TCL_REG_NOSUB as we may have backrefs
04445                      * or capture vars.
04446                      */
04447                     int cflags = TCL_REG_ADVANCED
04448                         | (noCase ? TCL_REG_NOCASE : 0);
04449                     TclEmitInstInt1(INST_REGEXP, cflags, envPtr);
04450                 }
04451                 break;
04452             }
04453             default:
04454                 Tcl_Panic("unknown switch mode: %d", mode);
04455             }
04456 
04457             /*
04458              * In a fall-through case, we will jump on _true_ to the place
04459              * where the body starts (generated later, with guarantee of this
04460              * ensured earlier; the final body is never a fall-through).
04461              */
04462 
04463             if (bodyToken[i+1]->size==1 && bodyToken[i+1]->start[0]=='-') {
04464                 if (contFixIndex == -1) {
04465                     contFixIndex = fixupCount;
04466                     contFixCount = 0;
04467                 }
04468                 TclEmitForwardJump(envPtr, TCL_TRUE_JUMP,
04469                         fixupArray+contFixIndex+contFixCount);
04470                 fixupCount++;
04471                 contFixCount++;
04472                 continue;
04473             }
04474 
04475             TclEmitForwardJump(envPtr, TCL_FALSE_JUMP, fixupArray+fixupCount);
04476             nextArmFixupIndex = fixupCount;
04477             fixupCount++;
04478         } else {
04479             /*
04480              * Got a default clause; set a flag to inhibit the generation of
04481              * the jump after the body and the cleanup of the intermediate
04482              * value that we are switching against.
04483              *
04484              * Note that default clauses (which are always terminal clauses)
04485              * cannot be fall-through clauses as well, since the last clause
04486              * is never a fall-through clause (which we have already
04487              * verified).
04488              */
04489 
04490             foundDefault = 1;
04491         }
04492 
04493         /*
04494          * Generate the body for the arm. This is guaranteed not to be a
04495          * fall-through case, but it might have preceding fall-through cases,
04496          * so we must process those first.
04497          */
04498 
04499         if (contFixIndex != -1) {
04500             int j;
04501 
04502             for (j=0 ; j<contFixCount ; j++) {
04503                 fixupTargetArray[contFixIndex+j] = CurrentOffset(envPtr);
04504             }
04505             contFixIndex = -1;
04506         }
04507 
04508         /*
04509          * Now do the actual compilation. Note that we do not use CompileBody
04510          * because we may have synthesized the tokens in a non-standard
04511          * pattern.
04512          */
04513 
04514         TclEmitOpcode(INST_POP, envPtr);
04515         envPtr->currStackDepth = savedStackDepth + 1;
04516         envPtr->line = bodyLines[i+1];          /* TIP #280 */
04517         TclCompileCmdWord(interp, bodyToken[i+1], 1, envPtr);
04518 
04519         if (!foundDefault) {
04520             TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP,
04521                     fixupArray+fixupCount);
04522             fixupCount++;
04523             fixupTargetArray[nextArmFixupIndex] = CurrentOffset(envPtr);
04524         }
04525     }
04526 
04527     /*
04528      * Clean up all our temporary space and return.
04529      */
04530 
04531     ckfree((char *) bodyToken);
04532     ckfree((char *) bodyLines);
04533     if (bodyTokenArray != NULL) {
04534         ckfree((char *) bodyTokenArray);
04535     }
04536 
04537     /*
04538      * Discard the value we are matching against unless we've had a default
04539      * clause (in which case it will already be gone due to the code at the
04540      * start of processing an arm, guaranteed) and make the result of the
04541      * command an empty string.
04542      */
04543 
04544     if (!foundDefault) {
04545         TclEmitOpcode(INST_POP, envPtr);
04546         PushLiteral(envPtr, "", 0);
04547     }
04548 
04549     /*
04550      * Do jump fixups for arms that were executed. First, fill in the jumps of
04551      * all jumps that don't point elsewhere to point to here.
04552      */
04553 
04554     for (i=0 ; i<fixupCount ; i++) {
04555         if (fixupTargetArray[i] == 0) {
04556             fixupTargetArray[i] = envPtr->codeNext-envPtr->codeStart;
04557         }
04558     }
04559 
04560     /*
04561      * Now scan backwards over all the jumps (all of which are forward jumps)
04562      * doing each one. When we do one and there is a size changes, we must
04563      * scan back over all the previous ones and see if they need adjusting
04564      * before proceeding with further jump fixups (the interleaved nature of
04565      * all the jumps makes this impossible to do without nested loops).
04566      */
04567 
04568     for (i=fixupCount-1 ; i>=0 ; i--) {
04569         if (TclFixupForwardJump(envPtr, &fixupArray[i],
04570                 fixupTargetArray[i] - fixupArray[i].codeOffset, 127)) {
04571             int j;
04572 
04573             for (j=i-1 ; j>=0 ; j--) {
04574                 if (fixupTargetArray[j] > fixupArray[i].codeOffset) {
04575                     fixupTargetArray[j] += 3;
04576                 }
04577             }
04578         }
04579     }
04580     ckfree((char *) fixupArray);
04581     ckfree((char *) fixupTargetArray);
04582 
04583     envPtr->currStackDepth = savedStackDepth + 1;
04584     return TCL_OK;
04585 }
04586 
04587 /*
04588  *----------------------------------------------------------------------
04589  *
04590  * DupJumptableInfo, FreeJumptableInfo --
04591  *
04592  *      Functions to duplicate, release and print a jump-table created for use
04593  *      with the INST_JUMP_TABLE instruction.
04594  *
04595  * Results:
04596  *      DupJumptableInfo: a copy of the jump-table
04597  *      FreeJumptableInfo: none
04598  *      PrintJumptableInfo: none
04599  *
04600  * Side effects:
04601  *      DupJumptableInfo: allocates memory
04602  *      FreeJumptableInfo: releases memory
04603  *      PrintJumptableInfo: none
04604  *
04605  *----------------------------------------------------------------------
04606  */
04607 
04608 static ClientData
04609 DupJumptableInfo(
04610     ClientData clientData)
04611 {
04612     JumptableInfo *jtPtr = clientData;
04613     JumptableInfo *newJtPtr = (JumptableInfo *)
04614             ckalloc(sizeof(JumptableInfo));
04615     Tcl_HashEntry *hPtr, *newHPtr;
04616     Tcl_HashSearch search;
04617     int isNew;
04618 
04619     Tcl_InitHashTable(&newJtPtr->hashTable, TCL_STRING_KEYS);
04620     hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search);
04621     while (hPtr != NULL) {
04622         newHPtr = Tcl_CreateHashEntry(&newJtPtr->hashTable,
04623                 Tcl_GetHashKey(&jtPtr->hashTable, hPtr), &isNew);
04624         Tcl_SetHashValue(newHPtr, Tcl_GetHashValue(hPtr));
04625     }
04626     return newJtPtr;
04627 }
04628 
04629 static void
04630 FreeJumptableInfo(
04631     ClientData clientData)
04632 {
04633     JumptableInfo *jtPtr = clientData;
04634 
04635     Tcl_DeleteHashTable(&jtPtr->hashTable);
04636     ckfree((char *) jtPtr);
04637 }
04638 
04639 static void
04640 PrintJumptableInfo(
04641     ClientData clientData,
04642     Tcl_Obj *appendObj,
04643     ByteCode *codePtr,
04644     unsigned int pcOffset)
04645 {
04646     register JumptableInfo *jtPtr = clientData;
04647     Tcl_HashEntry *hPtr;
04648     Tcl_HashSearch search;
04649     const char *keyPtr;
04650     int offset, i = 0;
04651 
04652     hPtr = Tcl_FirstHashEntry(&jtPtr->hashTable, &search);
04653     for (; hPtr ; hPtr = Tcl_NextHashEntry(&search)) {
04654         keyPtr = Tcl_GetHashKey(&jtPtr->hashTable, hPtr);
04655         offset = PTR2INT(Tcl_GetHashValue(hPtr));
04656 
04657         if (i++) {
04658             Tcl_AppendToObj(appendObj, ", ", -1);
04659             if (i%4==0) {
04660                 Tcl_AppendToObj(appendObj, "\n\t\t", -1);
04661             }
04662         }
04663         Tcl_AppendPrintfToObj(appendObj, "\"%s\"->pc %d",
04664                 keyPtr, pcOffset + offset);
04665     }
04666 }
04667 
04668 /*
04669  *----------------------------------------------------------------------
04670  *
04671  * TclCompileWhileCmd --
04672  *
04673  *      Procedure called to compile the "while" command.
04674  *
04675  * Results:
04676  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
04677  *      evaluation to runtime.
04678  *
04679  * Side effects:
04680  *      Instructions are added to envPtr to execute the "while" command at
04681  *      runtime.
04682  *
04683  *----------------------------------------------------------------------
04684  */
04685 
04686 int
04687 TclCompileWhileCmd(
04688     Tcl_Interp *interp,         /* Used for error reporting. */
04689     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
04690                                  * created by Tcl_ParseCommand. */
04691     Command *cmdPtr,            /* Points to defintion of command being
04692                                  * compiled. */
04693     CompileEnv *envPtr)         /* Holds resulting instructions. */
04694 {
04695     Tcl_Token *testTokenPtr, *bodyTokenPtr;
04696     JumpFixup jumpEvalCondFixup;
04697     int testCodeOffset, bodyCodeOffset, jumpDist, range, code, boolVal;
04698     int savedStackDepth = envPtr->currStackDepth;
04699     int loopMayEnd = 1;         /* This is set to 0 if it is recognized as an
04700                                  * infinite loop. */
04701     Tcl_Obj *boolObj;
04702     DefineLineInformation;      /* TIP #280 */
04703 
04704     if (parsePtr->numWords != 3) {
04705         return TCL_ERROR;
04706     }
04707 
04708     /*
04709      * If the test expression requires substitutions, don't compile the while
04710      * command inline. E.g., the expression might cause the loop to never
04711      * execute or execute forever, as in "while "$x < 5" {}".
04712      *
04713      * Bail out also if the body expression requires substitutions in order to
04714      * insure correct behaviour [Bug 219166]
04715      */
04716 
04717     testTokenPtr = TokenAfter(parsePtr->tokenPtr);
04718     bodyTokenPtr = TokenAfter(testTokenPtr);
04719 
04720     if ((testTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)
04721             || (bodyTokenPtr->type != TCL_TOKEN_SIMPLE_WORD)) {
04722         return TCL_ERROR;
04723     }
04724 
04725     /*
04726      * Find out if the condition is a constant.
04727      */
04728 
04729     boolObj = Tcl_NewStringObj(testTokenPtr[1].start, testTokenPtr[1].size);
04730     Tcl_IncrRefCount(boolObj);
04731     code = Tcl_GetBooleanFromObj(NULL, boolObj, &boolVal);
04732     TclDecrRefCount(boolObj);
04733     if (code == TCL_OK) {
04734         if (boolVal) {
04735             /*
04736              * It is an infinite loop; flag it so that we generate a more
04737              * efficient body.
04738              */
04739 
04740             loopMayEnd = 0;
04741         } else {
04742             /*
04743              * This is an empty loop: "while 0 {...}" or such. Compile no
04744              * bytecodes.
04745              */
04746 
04747             goto pushResult;
04748         }
04749     }
04750 
04751     /*
04752      * Create a ExceptionRange record for the loop body. This is used to
04753      * implement break and continue.
04754      */
04755 
04756     range = DeclareExceptionRange(envPtr, LOOP_EXCEPTION_RANGE);
04757 
04758     /*
04759      * Jump to the evaluation of the condition. This code uses the "loop
04760      * rotation" optimisation (which eliminates one branch from the loop).
04761      * "while cond body" produces then:
04762      *       goto A
04763      *    B: body                : bodyCodeOffset
04764      *    A: cond -> result      : testCodeOffset, continueOffset
04765      *       if (result) goto B
04766      *
04767      * The infinite loop "while 1 body" produces:
04768      *    B: body                : all three offsets here
04769      *       goto B
04770      */
04771 
04772     if (loopMayEnd) {
04773         TclEmitForwardJump(envPtr, TCL_UNCONDITIONAL_JUMP, &jumpEvalCondFixup);
04774         testCodeOffset = 0;     /* Avoid compiler warning. */
04775     } else {
04776         /*
04777          * Make sure that the first command in the body is preceded by an
04778          * INST_START_CMD, and hence counted properly. [Bug 1752146]
04779          */
04780 
04781         envPtr->atCmdStart = 0;
04782         testCodeOffset = CurrentOffset(envPtr);
04783     }
04784 
04785     /*
04786      * Compile the loop body.
04787      */
04788 
04789     envPtr->line = mapPtr->loc[eclIndex].line[2];
04790     bodyCodeOffset = ExceptionRangeStarts(envPtr, range);
04791     CompileBody(envPtr, bodyTokenPtr, interp);
04792     ExceptionRangeEnds(envPtr, range);
04793     envPtr->currStackDepth = savedStackDepth + 1;
04794     TclEmitOpcode(INST_POP, envPtr);
04795 
04796     /*
04797      * Compile the test expression then emit the conditional jump that
04798      * terminates the while. We already know it's a simple word.
04799      */
04800 
04801     if (loopMayEnd) {
04802         testCodeOffset = CurrentOffset(envPtr);
04803         jumpDist = testCodeOffset - jumpEvalCondFixup.codeOffset;
04804         if (TclFixupForwardJump(envPtr, &jumpEvalCondFixup, jumpDist, 127)) {
04805             bodyCodeOffset += 3;
04806             testCodeOffset += 3;
04807         }
04808         envPtr->currStackDepth = savedStackDepth;
04809         envPtr->line = mapPtr->loc[eclIndex].line[1];
04810         TclCompileExprWords(interp, testTokenPtr, 1, envPtr);
04811         envPtr->currStackDepth = savedStackDepth + 1;
04812 
04813         jumpDist = CurrentOffset(envPtr) - bodyCodeOffset;
04814         if (jumpDist > 127) {
04815             TclEmitInstInt4(INST_JUMP_TRUE4, -jumpDist, envPtr);
04816         } else {
04817             TclEmitInstInt1(INST_JUMP_TRUE1, -jumpDist, envPtr);
04818         }
04819     } else {
04820         jumpDist = CurrentOffset(envPtr) - bodyCodeOffset;
04821         if (jumpDist > 127) {
04822             TclEmitInstInt4(INST_JUMP4, -jumpDist, envPtr);
04823         } else {
04824             TclEmitInstInt1(INST_JUMP1, -jumpDist, envPtr);
04825         }
04826     }
04827 
04828     /*
04829      * Set the loop's body, continue and break offsets.
04830      */
04831 
04832     envPtr->exceptArrayPtr[range].continueOffset = testCodeOffset;
04833     envPtr->exceptArrayPtr[range].codeOffset = bodyCodeOffset;
04834     ExceptionRangeTarget(envPtr, range, breakOffset);
04835 
04836     /*
04837      * The while command's result is an empty string.
04838      */
04839 
04840   pushResult:
04841     envPtr->currStackDepth = savedStackDepth;
04842     PushLiteral(envPtr, "", 0);
04843     return TCL_OK;
04844 }
04845 
04846 /*
04847  *----------------------------------------------------------------------
04848  *
04849  * PushVarName --
04850  *
04851  *      Procedure used in the compiling where pushing a variable name is
04852  *      necessary (append, lappend, set).
04853  *
04854  * Results:
04855  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
04856  *      evaluation to runtime.
04857  *
04858  * Side effects:
04859  *      Instructions are added to envPtr to execute the "set" command at
04860  *      runtime.
04861  *
04862  *----------------------------------------------------------------------
04863  */
04864 
04865 static int
04866 PushVarName(
04867     Tcl_Interp *interp,         /* Used for error reporting. */
04868     Tcl_Token *varTokenPtr,     /* Points to a variable token. */
04869     CompileEnv *envPtr,         /* Holds resulting instructions. */
04870     int flags,                  /* TCL_CREATE_VAR or TCL_NO_LARGE_INDEX. */
04871     int *localIndexPtr,         /* Must not be NULL. */
04872     int *simpleVarNamePtr,      /* Must not be NULL. */
04873     int *isScalarPtr,           /* Must not be NULL. */
04874     int line)                   /* Line the token starts on. */
04875 {
04876     register const char *p;
04877     const char *name, *elName;
04878     register int i, n;
04879     Tcl_Token *elemTokenPtr = NULL;
04880     int nameChars, elNameChars, simpleVarName, localIndex;
04881     int elemTokenCount = 0, allocedTokens = 0, removedParen = 0;
04882 
04883     /*
04884      * Decide if we can use a frame slot for the var/array name or if we need
04885      * to emit code to compute and push the name at runtime. We use a frame
04886      * slot (entry in the array of local vars) if we are compiling a procedure
04887      * body and if the name is simple text that does not include namespace
04888      * qualifiers.
04889      */
04890 
04891     simpleVarName = 0;
04892     name = elName = NULL;
04893     nameChars = elNameChars = 0;
04894     localIndex = -1;
04895 
04896     /*
04897      * Check not only that the type is TCL_TOKEN_SIMPLE_WORD, but whether
04898      * curly braces surround the variable name. This really matters for array
04899      * elements to handle things like
04900      *    set {x($foo)} 5
04901      * which raises an undefined var error if we are not careful here.
04902      */
04903 
04904     if ((varTokenPtr->type == TCL_TOKEN_SIMPLE_WORD) &&
04905             (varTokenPtr->start[0] != '{')) {
04906         /*
04907          * A simple variable name. Divide it up into "name" and "elName"
04908          * strings. If it is not a local variable, look it up at runtime.
04909          */
04910 
04911         simpleVarName = 1;
04912 
04913         name = varTokenPtr[1].start;
04914         nameChars = varTokenPtr[1].size;
04915         if (name[nameChars-1] == ')') {
04916             /*
04917              * last char is ')' => potential array reference.
04918              */
04919 
04920             for (i=0,p=name ; i<nameChars ; i++,p++) {
04921                 if (*p == '(') {
04922                     elName = p + 1;
04923                     elNameChars = nameChars - i - 2;
04924                     nameChars = i;
04925                     break;
04926                 }
04927             }
04928 
04929             if ((elName != NULL) && elNameChars) {
04930                 /*
04931                  * An array element, the element name is a simple string:
04932                  * assemble the corresponding token.
04933                  */
04934 
04935                 elemTokenPtr = (Tcl_Token *) TclStackAlloc(interp,
04936                         sizeof(Tcl_Token));
04937                 allocedTokens = 1;
04938                 elemTokenPtr->type = TCL_TOKEN_TEXT;
04939                 elemTokenPtr->start = elName;
04940                 elemTokenPtr->size = elNameChars;
04941                 elemTokenPtr->numComponents = 0;
04942                 elemTokenCount = 1;
04943             }
04944         }
04945     } else if (((n = varTokenPtr->numComponents) > 1)
04946             && (varTokenPtr[1].type == TCL_TOKEN_TEXT)
04947             && (varTokenPtr[n].type == TCL_TOKEN_TEXT)
04948             && (varTokenPtr[n].start[varTokenPtr[n].size - 1] == ')')) {
04949 
04950         /*
04951          * Check for parentheses inside first token.
04952          */
04953 
04954         simpleVarName = 0;
04955         for (i = 0, p = varTokenPtr[1].start;
04956                 i < varTokenPtr[1].size; i++, p++) {
04957             if (*p == '(') {
04958                 simpleVarName = 1;
04959                 break;
04960             }
04961         }
04962         if (simpleVarName) {
04963             int remainingChars;
04964 
04965             /*
04966              * Check the last token: if it is just ')', do not count it.
04967              * Otherwise, remove the ')' and flag so that it is restored at
04968              * the end.
04969              */
04970 
04971             if (varTokenPtr[n].size == 1) {
04972                 --n;
04973             } else {
04974                 --varTokenPtr[n].size;
04975                 removedParen = n;
04976             }
04977 
04978             name = varTokenPtr[1].start;
04979             nameChars = p - varTokenPtr[1].start;
04980             elName = p + 1;
04981             remainingChars = (varTokenPtr[2].start - p) - 1;
04982             elNameChars = (varTokenPtr[n].start - p) + varTokenPtr[n].size - 2;
04983 
04984             if (remainingChars) {
04985                 /*
04986                  * Make a first token with the extra characters in the first
04987                  * token.
04988                  */
04989 
04990                 elemTokenPtr = (Tcl_Token *) TclStackAlloc(interp,
04991                         n * sizeof(Tcl_Token));
04992                 allocedTokens = 1;
04993                 elemTokenPtr->type = TCL_TOKEN_TEXT;
04994                 elemTokenPtr->start = elName;
04995                 elemTokenPtr->size = remainingChars;
04996                 elemTokenPtr->numComponents = 0;
04997                 elemTokenCount = n;
04998 
04999                 /*
05000                  * Copy the remaining tokens.
05001                  */
05002 
05003                 memcpy(elemTokenPtr+1, varTokenPtr+2,
05004                         (n-1) * sizeof(Tcl_Token));
05005             } else {
05006                 /*
05007                  * Use the already available tokens.
05008                  */
05009 
05010                 elemTokenPtr = &varTokenPtr[2];
05011                 elemTokenCount = n - 1;
05012             }
05013         }
05014     }
05015 
05016     if (simpleVarName) {
05017         /*
05018          * See whether name has any namespace separators (::'s).
05019          */
05020 
05021         int hasNsQualifiers = 0;
05022         for (i = 0, p = name;  i < nameChars;  i++, p++) {
05023             if ((*p == ':') && ((i+1) < nameChars) && (*(p+1) == ':')) {
05024                 hasNsQualifiers = 1;
05025                 break;
05026             }
05027         }
05028 
05029         /*
05030          * Look up the var name's index in the array of local vars in the proc
05031          * frame. If retrieving the var's value and it doesn't already exist,
05032          * push its name and look it up at runtime.
05033          */
05034 
05035         if ((envPtr->procPtr != NULL) && !hasNsQualifiers) {
05036             localIndex = TclFindCompiledLocal(name, nameChars,
05037                     /*create*/ flags & TCL_CREATE_VAR,
05038                     envPtr->procPtr);
05039             if ((flags & TCL_NO_LARGE_INDEX) && (localIndex > 255)) {
05040                 /*
05041                  * We'll push the name.
05042                  */
05043 
05044                 localIndex = -1;
05045             }
05046         }
05047         if (localIndex < 0) {
05048             PushLiteral(envPtr, name, nameChars);
05049         }
05050 
05051         /*
05052          * Compile the element script, if any.
05053          */
05054 
05055         if (elName != NULL) {
05056             if (elNameChars) {
05057                 envPtr->line = line;
05058                 TclCompileTokens(interp, elemTokenPtr, elemTokenCount, envPtr);
05059             } else {
05060                 PushLiteral(envPtr, "", 0);
05061             }
05062         }
05063     } else {
05064         /*
05065          * The var name isn't simple: compile and push it.
05066          */
05067 
05068         envPtr->line = line;
05069         CompileTokens(envPtr, varTokenPtr, interp);
05070     }
05071 
05072     if (removedParen) {
05073         ++varTokenPtr[removedParen].size;
05074     }
05075     if (allocedTokens) {
05076         TclStackFree(interp, elemTokenPtr);
05077     }
05078     *localIndexPtr = localIndex;
05079     *simpleVarNamePtr = simpleVarName;
05080     *isScalarPtr = (elName == NULL);
05081     return TCL_OK;
05082 }
05083 
05084 /*
05085  *----------------------------------------------------------------------
05086  *
05087  * CompileUnaryOpCmd --
05088  *
05089  *      Utility routine to compile the unary operator commands.
05090  *
05091  * Results:
05092  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05093  *      evaluation to runtime.
05094  *
05095  * Side effects:
05096  *      Instructions are added to envPtr to execute the compiled command at
05097  *      runtime.
05098  *
05099  *----------------------------------------------------------------------
05100  */
05101 
05102 static int
05103 CompileUnaryOpCmd(
05104     Tcl_Interp *interp,
05105     Tcl_Parse *parsePtr,
05106     int instruction,
05107     CompileEnv *envPtr)
05108 {
05109     Tcl_Token *tokenPtr;
05110     DefineLineInformation;      /* TIP #280 */
05111 
05112     if (parsePtr->numWords != 2) {
05113         return TCL_ERROR;
05114     }
05115     tokenPtr = TokenAfter(parsePtr->tokenPtr);
05116     CompileWord(envPtr, tokenPtr, interp, 1);
05117     TclEmitOpcode(instruction, envPtr);
05118     return TCL_OK;
05119 }
05120 
05121 /*
05122  *----------------------------------------------------------------------
05123  *
05124  * CompileAssociativeBinaryOpCmd --
05125  *
05126  *      Utility routine to compile the binary operator commands that accept an
05127  *      arbitrary number of arguments, and that are associative operations.
05128  *      Because of the associativity, we may combine operations from right to
05129  *      left, saving us any effort of re-ordering the arguments on the stack
05130  *      after substitutions are completed.
05131  *
05132  * Results:
05133  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05134  *      evaluation to runtime.
05135  *
05136  * Side effects:
05137  *      Instructions are added to envPtr to execute the compiled command at
05138  *      runtime.
05139  *
05140  *----------------------------------------------------------------------
05141  */
05142 
05143 static int
05144 CompileAssociativeBinaryOpCmd(
05145     Tcl_Interp *interp,
05146     Tcl_Parse *parsePtr,
05147     const char *identity,
05148     int instruction,
05149     CompileEnv *envPtr)
05150 {
05151     Tcl_Token *tokenPtr = parsePtr->tokenPtr;
05152     DefineLineInformation;      /* TIP #280 */
05153     int words;
05154 
05155     for (words=1 ; words<parsePtr->numWords ; words++) {
05156         tokenPtr = TokenAfter(tokenPtr);
05157         CompileWord(envPtr, tokenPtr, interp, words);
05158     }
05159     if (parsePtr->numWords <= 2) {
05160         PushLiteral(envPtr, identity, -1);
05161         words++;
05162     }
05163     if (words > 3) {
05164         /*
05165          * Reverse order of arguments to get precise agreement with
05166          * [expr] in calcuations, including roundoff errors.
05167          */
05168         TclEmitInstInt4(INST_REVERSE, words-1, envPtr);
05169     }
05170     while (--words > 1) {
05171         TclEmitOpcode(instruction, envPtr);
05172     }
05173     return TCL_OK;
05174 }
05175 
05176 /*
05177  *----------------------------------------------------------------------
05178  *
05179  * CompileStrictlyBinaryOpCmd --
05180  *
05181  *      Utility routine to compile the binary operator commands, that strictly
05182  *      accept exactly two arguments.
05183  *
05184  * Results:
05185  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05186  *      evaluation to runtime.
05187  *
05188  * Side effects:
05189  *      Instructions are added to envPtr to execute the compiled command at
05190  *      runtime.
05191  *
05192  *----------------------------------------------------------------------
05193  */
05194 
05195 static int
05196 CompileStrictlyBinaryOpCmd(
05197     Tcl_Interp *interp,
05198     Tcl_Parse *parsePtr,
05199     int instruction,
05200     CompileEnv *envPtr)
05201 {
05202     if (parsePtr->numWords != 3) {
05203         return TCL_ERROR;
05204     }
05205     return CompileAssociativeBinaryOpCmd(interp, parsePtr,
05206             NULL, instruction, envPtr);
05207 }
05208 
05209 /*
05210  *----------------------------------------------------------------------
05211  *
05212  * CompileComparisonOpCmd --
05213  *
05214  *      Utility routine to compile the n-ary comparison operator commands.
05215  *
05216  * Results:
05217  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05218  *      evaluation to runtime.
05219  *
05220  * Side effects:
05221  *      Instructions are added to envPtr to execute the compiled command at
05222  *      runtime.
05223  *
05224  *----------------------------------------------------------------------
05225  */
05226 
05227 static int
05228 CompileComparisonOpCmd(
05229     Tcl_Interp *interp,
05230     Tcl_Parse *parsePtr,
05231     int instruction,
05232     CompileEnv *envPtr)
05233 {
05234     Tcl_Token *tokenPtr;
05235     DefineLineInformation;      /* TIP #280 */
05236 
05237     if (parsePtr->numWords < 3) {
05238         PushLiteral(envPtr, "1", 1);
05239     } else if (parsePtr->numWords == 3) {
05240         tokenPtr = TokenAfter(parsePtr->tokenPtr);
05241         CompileWord(envPtr, tokenPtr, interp, 1);
05242         tokenPtr = TokenAfter(tokenPtr);
05243         CompileWord(envPtr, tokenPtr, interp, 2);
05244         TclEmitOpcode(instruction, envPtr);
05245     } else if (envPtr->procPtr == NULL) {
05246         /*
05247          * No local variable space!
05248          */
05249 
05250         return TCL_ERROR;
05251     } else {
05252         int tmpIndex = TclFindCompiledLocal(NULL, 0, 1, envPtr->procPtr);
05253         int words;
05254 
05255         tokenPtr = TokenAfter(parsePtr->tokenPtr);
05256         CompileWord(envPtr, tokenPtr, interp, 1);
05257         tokenPtr = TokenAfter(tokenPtr);
05258         CompileWord(envPtr, tokenPtr, interp, 2);
05259         if (tmpIndex <= 255) {
05260             TclEmitInstInt1(INST_STORE_SCALAR1, tmpIndex, envPtr);
05261         } else {
05262             TclEmitInstInt4(INST_STORE_SCALAR4, tmpIndex, envPtr);
05263         }
05264         TclEmitOpcode(instruction, envPtr);
05265         for (words=3 ; words<parsePtr->numWords ;) {
05266             if (tmpIndex <= 255) {
05267                 TclEmitInstInt1(INST_LOAD_SCALAR1, tmpIndex, envPtr);
05268             } else {
05269                 TclEmitInstInt4(INST_LOAD_SCALAR4, tmpIndex, envPtr);
05270             }
05271             tokenPtr = TokenAfter(tokenPtr);
05272             CompileWord(envPtr, tokenPtr, interp, words);
05273             if (++words < parsePtr->numWords) {
05274                 if (tmpIndex <= 255) {
05275                     TclEmitInstInt1(INST_STORE_SCALAR1, tmpIndex, envPtr);
05276                 } else {
05277                     TclEmitInstInt4(INST_STORE_SCALAR4, tmpIndex, envPtr);
05278                 }
05279             }
05280             TclEmitOpcode(instruction, envPtr);
05281         }
05282         for (; words>3 ; words--) {
05283             TclEmitOpcode(INST_BITAND, envPtr);
05284         }
05285 
05286         /*
05287          * Drop the value from the temp variable; retaining that reference
05288          * might be expensive elsewhere.
05289          */
05290 
05291         PushLiteral(envPtr, "", 0);
05292         if (tmpIndex <= 255) {
05293             TclEmitInstInt1(INST_STORE_SCALAR1, tmpIndex, envPtr);
05294         } else {
05295             TclEmitInstInt4(INST_STORE_SCALAR4, tmpIndex, envPtr);
05296         }
05297         TclEmitOpcode(INST_POP, envPtr);
05298     }
05299     return TCL_OK;
05300 }
05301 
05302 /*
05303  *----------------------------------------------------------------------
05304  *
05305  * TclCompile*OpCmd --
05306  *
05307  *      Procedures called to compile the corresponding "::tcl::mathop::*"
05308  *      commands. These are all wrappers around the utility operator command
05309  *      compiler functions, except for the compilers for subtraction and
05310  *      division, which are special.
05311  *
05312  * Results:
05313  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05314  *      evaluation to runtime.
05315  *
05316  * Side effects:
05317  *      Instructions are added to envPtr to execute the compiled command at
05318  *      runtime.
05319  *
05320  *----------------------------------------------------------------------
05321  */
05322 
05323 int
05324 TclCompileInvertOpCmd(
05325     Tcl_Interp *interp,
05326     Tcl_Parse *parsePtr,
05327     Command *cmdPtr,            /* Points to defintion of command being
05328                                  * compiled. */
05329     CompileEnv *envPtr)
05330 {
05331     return CompileUnaryOpCmd(interp, parsePtr, INST_BITNOT, envPtr);
05332 }
05333 
05334 int
05335 TclCompileNotOpCmd(
05336     Tcl_Interp *interp,
05337     Tcl_Parse *parsePtr,
05338     Command *cmdPtr,            /* Points to defintion of command being
05339                                  * compiled. */
05340     CompileEnv *envPtr)
05341 {
05342     return CompileUnaryOpCmd(interp, parsePtr, INST_LNOT, envPtr);
05343 }
05344 
05345 int
05346 TclCompileAddOpCmd(
05347     Tcl_Interp *interp,
05348     Tcl_Parse *parsePtr,
05349     Command *cmdPtr,            /* Points to defintion of command being
05350                                  * compiled. */
05351     CompileEnv *envPtr)
05352 {
05353     return CompileAssociativeBinaryOpCmd(interp, parsePtr, "0", INST_ADD,
05354             envPtr);
05355 }
05356 
05357 int
05358 TclCompileMulOpCmd(
05359     Tcl_Interp *interp,
05360     Tcl_Parse *parsePtr,
05361     Command *cmdPtr,            /* Points to defintion of command being
05362                                  * compiled. */
05363     CompileEnv *envPtr)
05364 {
05365     return CompileAssociativeBinaryOpCmd(interp, parsePtr, "1", INST_MULT,
05366             envPtr);
05367 }
05368 
05369 int
05370 TclCompileAndOpCmd(
05371     Tcl_Interp *interp,
05372     Tcl_Parse *parsePtr,
05373     Command *cmdPtr,            /* Points to defintion of command being
05374                                  * compiled. */
05375     CompileEnv *envPtr)
05376 {
05377     return CompileAssociativeBinaryOpCmd(interp, parsePtr, "-1", INST_BITAND,
05378             envPtr);
05379 }
05380 
05381 int
05382 TclCompileOrOpCmd(
05383     Tcl_Interp *interp,
05384     Tcl_Parse *parsePtr,
05385     Command *cmdPtr,            /* Points to defintion of command being
05386                                  * compiled. */
05387     CompileEnv *envPtr)
05388 {
05389     return CompileAssociativeBinaryOpCmd(interp, parsePtr, "0", INST_BITOR,
05390             envPtr);
05391 }
05392 
05393 int
05394 TclCompileXorOpCmd(
05395     Tcl_Interp *interp,
05396     Tcl_Parse *parsePtr,
05397     Command *cmdPtr,            /* Points to defintion of command being
05398                                  * compiled. */
05399     CompileEnv *envPtr)
05400 {
05401     return CompileAssociativeBinaryOpCmd(interp, parsePtr, "0", INST_BITXOR,
05402             envPtr);
05403 }
05404 
05405 int
05406 TclCompilePowOpCmd(
05407     Tcl_Interp *interp,
05408     Tcl_Parse *parsePtr,
05409     Command *cmdPtr,            /* Points to defintion of command being
05410                                  * compiled. */
05411     CompileEnv *envPtr)
05412 {
05413     /*
05414      * This one has its own implementation because the ** operator is
05415      * the only one with right associativity.
05416      */
05417     Tcl_Token *tokenPtr = parsePtr->tokenPtr;
05418     DefineLineInformation;      /* TIP #280 */
05419     int words;
05420 
05421     for (words=1 ; words<parsePtr->numWords ; words++) {
05422         tokenPtr = TokenAfter(tokenPtr);
05423         CompileWord(envPtr, tokenPtr, interp, words);
05424     }
05425     if (parsePtr->numWords <= 2) {
05426         PushLiteral(envPtr, "1", 1);
05427         words++;
05428     }
05429     while (--words > 1) {
05430         TclEmitOpcode(INST_EXPON, envPtr);
05431     }
05432     return TCL_OK;
05433 }
05434 
05435 int
05436 TclCompileLshiftOpCmd(
05437     Tcl_Interp *interp,
05438     Tcl_Parse *parsePtr,
05439     Command *cmdPtr,            /* Points to defintion of command being
05440                                  * compiled. */
05441     CompileEnv *envPtr)
05442 {
05443     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_LSHIFT, envPtr);
05444 }
05445 
05446 int
05447 TclCompileRshiftOpCmd(
05448     Tcl_Interp *interp,
05449     Tcl_Parse *parsePtr,
05450     Command *cmdPtr,            /* Points to defintion of command being
05451                                  * compiled. */
05452     CompileEnv *envPtr)
05453 {
05454     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_RSHIFT, envPtr);
05455 }
05456 
05457 int
05458 TclCompileModOpCmd(
05459     Tcl_Interp *interp,
05460     Tcl_Parse *parsePtr,
05461     Command *cmdPtr,            /* Points to defintion of command being
05462                                  * compiled. */
05463     CompileEnv *envPtr)
05464 {
05465     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_MOD, envPtr);
05466 }
05467 
05468 int
05469 TclCompileNeqOpCmd(
05470     Tcl_Interp *interp,
05471     Tcl_Parse *parsePtr,
05472     Command *cmdPtr,            /* Points to defintion of command being
05473                                  * compiled. */
05474     CompileEnv *envPtr)
05475 {
05476     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_NEQ, envPtr);
05477 }
05478 
05479 int
05480 TclCompileStrneqOpCmd(
05481     Tcl_Interp *interp,
05482     Tcl_Parse *parsePtr,
05483     Command *cmdPtr,            /* Points to defintion of command being
05484                                  * compiled. */
05485     CompileEnv *envPtr)
05486 {
05487     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_STR_NEQ, envPtr);
05488 }
05489 
05490 int
05491 TclCompileInOpCmd(
05492     Tcl_Interp *interp,
05493     Tcl_Parse *parsePtr,
05494     Command *cmdPtr,            /* Points to defintion of command being
05495                                  * compiled. */
05496     CompileEnv *envPtr)
05497 {
05498     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_LIST_IN, envPtr);
05499 }
05500 
05501 int
05502 TclCompileNiOpCmd(
05503     Tcl_Interp *interp,
05504     Tcl_Parse *parsePtr,
05505     Command *cmdPtr,            /* Points to defintion of command being
05506                                  * compiled. */
05507     CompileEnv *envPtr)
05508 {
05509     return CompileStrictlyBinaryOpCmd(interp, parsePtr, INST_LIST_NOT_IN,
05510             envPtr);
05511 }
05512 
05513 int
05514 TclCompileLessOpCmd(
05515     Tcl_Interp *interp,
05516     Tcl_Parse *parsePtr,
05517     Command *cmdPtr,            /* Points to defintion of command being
05518                                  * compiled. */
05519     CompileEnv *envPtr)
05520 {
05521     return CompileComparisonOpCmd(interp, parsePtr, INST_LT, envPtr);
05522 }
05523 
05524 int
05525 TclCompileLeqOpCmd(
05526     Tcl_Interp *interp,
05527     Tcl_Parse *parsePtr,
05528     Command *cmdPtr,            /* Points to defintion of command being
05529                                  * compiled. */
05530     CompileEnv *envPtr)
05531 {
05532     return CompileComparisonOpCmd(interp, parsePtr, INST_LE, envPtr);
05533 }
05534 
05535 int
05536 TclCompileGreaterOpCmd(
05537     Tcl_Interp *interp,
05538     Tcl_Parse *parsePtr,
05539     Command *cmdPtr,            /* Points to defintion of command being
05540                                  * compiled. */
05541     CompileEnv *envPtr)
05542 {
05543     return CompileComparisonOpCmd(interp, parsePtr, INST_GT, envPtr);
05544 }
05545 
05546 int
05547 TclCompileGeqOpCmd(
05548     Tcl_Interp *interp,
05549     Tcl_Parse *parsePtr,
05550     Command *cmdPtr,            /* Points to defintion of command being
05551                                  * compiled. */
05552     CompileEnv *envPtr)
05553 {
05554     return CompileComparisonOpCmd(interp, parsePtr, INST_GE, envPtr);
05555 }
05556 
05557 int
05558 TclCompileEqOpCmd(
05559     Tcl_Interp *interp,
05560     Tcl_Parse *parsePtr,
05561     Command *cmdPtr,            /* Points to defintion of command being
05562                                  * compiled. */
05563     CompileEnv *envPtr)
05564 {
05565     return CompileComparisonOpCmd(interp, parsePtr, INST_EQ, envPtr);
05566 }
05567 
05568 int
05569 TclCompileStreqOpCmd(
05570     Tcl_Interp *interp,
05571     Tcl_Parse *parsePtr,
05572     Command *cmdPtr,            /* Points to defintion of command being
05573                                  * compiled. */
05574     CompileEnv *envPtr)
05575 {
05576     return CompileComparisonOpCmd(interp, parsePtr, INST_STR_EQ, envPtr);
05577 }
05578 
05579 int
05580 TclCompileMinusOpCmd(
05581     Tcl_Interp *interp,
05582     Tcl_Parse *parsePtr,
05583     Command *cmdPtr,            /* Points to defintion of command being
05584                                  * compiled. */
05585     CompileEnv *envPtr)
05586 {
05587     Tcl_Token *tokenPtr = parsePtr->tokenPtr;
05588     DefineLineInformation;      /* TIP #280 */
05589     int words;
05590 
05591     if (parsePtr->numWords == 1) {
05592         /* Fallback to direct eval to report syntax error */
05593         return TCL_ERROR;
05594     }
05595     for (words=1 ; words<parsePtr->numWords ; words++) {
05596         tokenPtr = TokenAfter(tokenPtr);
05597         CompileWord(envPtr, tokenPtr, interp, words);
05598     }
05599     if (words == 2) {
05600         TclEmitOpcode(INST_UMINUS, envPtr);
05601         return TCL_OK;
05602     }
05603     if (words == 3) {
05604         TclEmitOpcode(INST_SUB, envPtr);
05605         return TCL_OK;
05606     }
05607     /*
05608      * Reverse order of arguments to get precise agreement with
05609      * [expr] in calcuations, including roundoff errors.
05610      */
05611     TclEmitInstInt4(INST_REVERSE, words-1, envPtr);
05612     while (--words > 1) {
05613         TclEmitInstInt4(INST_REVERSE, 2, envPtr);
05614         TclEmitOpcode(INST_SUB, envPtr);
05615     }
05616     return TCL_OK;
05617 }
05618 
05619 int
05620 TclCompileDivOpCmd(
05621     Tcl_Interp *interp,
05622     Tcl_Parse *parsePtr,
05623     Command *cmdPtr,            /* Points to defintion of command being
05624                                  * compiled. */
05625     CompileEnv *envPtr)
05626 {
05627     Tcl_Token *tokenPtr = parsePtr->tokenPtr;
05628     DefineLineInformation;      /* TIP #280 */
05629     int words;
05630 
05631     if (parsePtr->numWords == 1) {
05632         /* Fallback to direct eval to report syntax error */
05633         return TCL_ERROR;
05634     }
05635     if (parsePtr->numWords == 2) {
05636         PushLiteral(envPtr, "1.0", 3);
05637     }
05638     for (words=1 ; words<parsePtr->numWords ; words++) {
05639         tokenPtr = TokenAfter(tokenPtr);
05640         CompileWord(envPtr, tokenPtr, interp, words);
05641     }
05642     if (words <= 3) {
05643         TclEmitOpcode(INST_DIV, envPtr);
05644         return TCL_OK;
05645     }
05646     /*
05647      * Reverse order of arguments to get precise agreement with
05648      * [expr] in calcuations, including roundoff errors.
05649      */
05650     TclEmitInstInt4(INST_REVERSE, words-1, envPtr);
05651     while (--words > 1) {
05652         TclEmitInstInt4(INST_REVERSE, 2, envPtr);
05653         TclEmitOpcode(INST_DIV, envPtr);
05654     }
05655     return TCL_OK;
05656 }
05657 
05658 /*
05659  *----------------------------------------------------------------------
05660  *
05661  * IndexTailVarIfKnown --
05662  *
05663  *      Procedure used in compiling [global] and [variable] commands. It
05664  *      inspects the variable name described by varTokenPtr and, if the tail
05665  *      is known at compile time, defines a corresponding local variable.
05666  *
05667  * Results:
05668  *      Returns the variable's index in the table of compiled locals if the
05669  *      tail is known at compile time, or -1 otherwise.
05670  *
05671  * Side effects:
05672  *      None.
05673  *
05674  *----------------------------------------------------------------------
05675  */
05676 
05677 static int
05678 IndexTailVarIfKnown(
05679     Tcl_Interp *interp,
05680     Tcl_Token *varTokenPtr,     /* Token representing the variable name */
05681     CompileEnv *envPtr)         /* Holds resulting instructions. */
05682 {
05683     Tcl_Obj *tailPtr;
05684     const char *tailName, *p;
05685     int len, n = varTokenPtr->numComponents;
05686     Tcl_Token *lastTokenPtr;
05687     int full, localIndex;
05688 
05689     /*
05690      * Determine if the tail is (a) known at compile time, and (b) not an
05691      * array element. Should any of these fail, return an error so that
05692      * the non-compiled command will be called at runtime.
05693      * In order for the tail to be known at compile time, the last token
05694      * in the word has to be constant and contain "::" if it is not the
05695      * only one.
05696      */
05697 
05698     if (envPtr->procPtr == NULL) {
05699         return -1;
05700     }
05701 
05702     TclNewObj(tailPtr);
05703     if (TclWordKnownAtCompileTime(varTokenPtr, tailPtr)) {
05704         full = 1;
05705         lastTokenPtr = varTokenPtr;
05706     } else {
05707         full = 0;
05708         lastTokenPtr = varTokenPtr + n;
05709         if (!TclWordKnownAtCompileTime(lastTokenPtr, tailPtr)) {
05710             Tcl_DecrRefCount(tailPtr);
05711             return -1;
05712         }
05713     }
05714 
05715     tailName = TclGetStringFromObj(tailPtr, &len);
05716 
05717     if (len) {
05718         if (*(tailName+len-1) == ')') {
05719             /*
05720              * Possible array: bail out
05721              */
05722 
05723             Tcl_DecrRefCount(tailPtr);
05724             return -1;
05725         }
05726 
05727         /*
05728          * Get the tail: immediately after the last '::'
05729          */
05730 
05731         for(p = tailName + len -1; p > tailName; p--) {
05732             if ((*p == ':') && (*(p-1) == ':')) {
05733                 p++;
05734                 break;
05735             }
05736         }
05737         if (!full && (p == tailName)) {
05738             /*
05739              * No :: in the last component
05740              */
05741             Tcl_DecrRefCount(tailPtr);
05742             return -1;
05743         }
05744         len -= p - tailName;
05745         tailName = p;
05746     }
05747 
05748     localIndex = TclFindCompiledLocal(tailName, len,
05749             /*create*/ TCL_CREATE_VAR,
05750             envPtr->procPtr);
05751     Tcl_DecrRefCount(tailPtr);
05752     return localIndex;
05753 }
05754 
05755 /*
05756  *----------------------------------------------------------------------
05757  *
05758  * TclCompileUpvarCmd --
05759  *
05760  *      Procedure called to compile the "upvar" command.
05761  *
05762  * Results:
05763  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05764  *      evaluation to runtime.
05765  *
05766  * Side effects:
05767  *      Instructions are added to envPtr to execute the "upvar" command at
05768  *      runtime.
05769  *
05770  *----------------------------------------------------------------------
05771  */
05772 
05773 int
05774 TclCompileUpvarCmd(
05775     Tcl_Interp *interp,         /* Used for error reporting. */
05776     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
05777                                  * created by Tcl_ParseCommand. */
05778     Command *cmdPtr,            /* Points to defintion of command being
05779                                  * compiled. */
05780     CompileEnv *envPtr)         /* Holds resulting instructions. */
05781 {
05782     Tcl_Token *tokenPtr, *otherTokenPtr, *localTokenPtr;
05783     int simpleVarName, isScalar, localIndex, numWords, i;
05784     DefineLineInformation;      /* TIP #280 */
05785     Tcl_Obj *objPtr = Tcl_NewObj();
05786 
05787     if (envPtr->procPtr == NULL) {
05788         Tcl_DecrRefCount(objPtr);
05789         return TCL_ERROR;
05790     }
05791 
05792     numWords = parsePtr->numWords;
05793     if (numWords < 3) {
05794         Tcl_DecrRefCount(objPtr);
05795         return TCL_ERROR;
05796     }
05797 
05798     /*
05799      * Push the frame index if it is known at compile time
05800      */
05801 
05802     tokenPtr = TokenAfter(parsePtr->tokenPtr);
05803     if(TclWordKnownAtCompileTime(tokenPtr, objPtr)) {
05804         CallFrame *framePtr;
05805         Tcl_ObjType *newTypePtr, *typePtr = objPtr->typePtr;
05806 
05807         /*
05808          * Attempt to convert to a level reference. Note that TclObjGetFrame
05809          * only changes the obj type when a conversion was successful.
05810          */
05811 
05812         TclObjGetFrame(interp, objPtr, &framePtr);
05813         newTypePtr = objPtr->typePtr;
05814         Tcl_DecrRefCount(objPtr);
05815 
05816         if (newTypePtr != typePtr) {
05817             if(numWords%2) {
05818                 return TCL_ERROR;
05819             }
05820             CompileWord(envPtr, tokenPtr, interp, 1);
05821             otherTokenPtr = TokenAfter(tokenPtr);
05822             i = 4;
05823         } else {
05824             if(!(numWords%2)) {
05825                 return TCL_ERROR;
05826             }
05827             PushLiteral(envPtr, "1", 1);
05828             otherTokenPtr = tokenPtr;
05829             i = 3;
05830         }
05831     } else {
05832         Tcl_DecrRefCount(objPtr);
05833         return TCL_ERROR;
05834     }
05835 
05836     /*
05837      * Loop over the (otherVar, thisVar) pairs. If any of the thisVar is not a
05838      * local variable, return an error so that the non-compiled command will
05839      * be called at runtime.
05840      */
05841 
05842     for(; i<=numWords; i+=2, otherTokenPtr = TokenAfter(localTokenPtr)) {
05843         localTokenPtr = TokenAfter(otherTokenPtr);
05844 
05845         CompileWord(envPtr, otherTokenPtr, interp, 1);
05846         PushVarName(interp, localTokenPtr, envPtr, TCL_CREATE_VAR,
05847                 &localIndex, &simpleVarName, &isScalar,
05848                 mapPtr->loc[eclIndex].line[1]);
05849 
05850         if((localIndex < 0) || !isScalar) {
05851             return TCL_ERROR;
05852         }
05853         TclEmitInstInt4(INST_UPVAR, localIndex, envPtr);
05854     }
05855 
05856     /*
05857      * Pop the frame index, and set the result to empty
05858      */
05859 
05860     TclEmitOpcode(INST_POP, envPtr);
05861     PushLiteral(envPtr, "", 0);
05862     return TCL_OK;
05863 }
05864 
05865 /*
05866  *----------------------------------------------------------------------
05867  *
05868  * TclCompileNamespaceCmd --
05869  *
05870  *      Procedure called to compile the "namespace" command; currently, only
05871  *      the subcommand "namespace upvar" is compiled to bytecodes.
05872  *
05873  * Results:
05874  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05875  *      evaluation to runtime.
05876  *
05877  * Side effects:
05878  *      Instructions are added to envPtr to execute the "namespace upvar"
05879  *      command at runtime.
05880  *
05881  *----------------------------------------------------------------------
05882  */
05883 
05884 int
05885 TclCompileNamespaceCmd(
05886     Tcl_Interp *interp,         /* Used for error reporting. */
05887     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
05888                                  * created by Tcl_ParseCommand. */
05889     Command *cmdPtr,            /* Points to defintion of command being
05890                                  * compiled. */
05891     CompileEnv *envPtr)         /* Holds resulting instructions. */
05892 {
05893     Tcl_Token *tokenPtr, *otherTokenPtr, *localTokenPtr;
05894     int simpleVarName, isScalar, localIndex, numWords, i;
05895     DefineLineInformation;      /* TIP #280 */
05896 
05897     if (envPtr->procPtr == NULL) {
05898         return TCL_ERROR;
05899     }
05900 
05901     /*
05902      * Only compile [namespace upvar ...]: needs an odd number of args, >=5
05903      */
05904 
05905     numWords = parsePtr->numWords;
05906     if (!(numWords%2) || (numWords < 5)) {
05907         return TCL_ERROR;
05908     }
05909 
05910     /*
05911      * Check if the second argument is "upvar"
05912      */
05913 
05914     tokenPtr = TokenAfter(parsePtr->tokenPtr);
05915     if ((tokenPtr->size != 5)  /* 5 == strlen("upvar") */
05916             || strncmp(tokenPtr->start, "upvar", 5)) {
05917         return TCL_ERROR;
05918     }
05919 
05920     /*
05921      * Push the namespace
05922      */
05923 
05924     tokenPtr = TokenAfter(tokenPtr);
05925     CompileWord(envPtr, tokenPtr, interp, 1);
05926 
05927     /*
05928      * Loop over the (otherVar, thisVar) pairs. If any of the thisVar is not a
05929      * local variable, return an error so that the non-compiled command will
05930      * be called at runtime.
05931      */
05932 
05933     localTokenPtr = tokenPtr;
05934     for(i=4; i<=numWords; i+=2) {
05935         otherTokenPtr = TokenAfter(localTokenPtr);
05936         localTokenPtr = TokenAfter(otherTokenPtr);
05937 
05938         CompileWord(envPtr, otherTokenPtr, interp, 1);
05939         PushVarName(interp, localTokenPtr, envPtr, TCL_CREATE_VAR,
05940                 &localIndex, &simpleVarName, &isScalar,
05941                 mapPtr->loc[eclIndex].line[1]);
05942 
05943         if((localIndex < 0) || !isScalar) {
05944             return TCL_ERROR;
05945         }
05946         TclEmitInstInt4(INST_NSUPVAR, localIndex, envPtr);
05947     }
05948 
05949     /*
05950      * Pop the namespace, and set the result to empty
05951      */
05952 
05953     TclEmitOpcode(INST_POP, envPtr);
05954     PushLiteral(envPtr, "", 0);
05955     return TCL_OK;
05956 }
05957 
05958 /*
05959  *----------------------------------------------------------------------
05960  *
05961  * TclCompileGlobalCmd --
05962  *
05963  *      Procedure called to compile the "global" command.
05964  *
05965  * Results:
05966  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
05967  *      evaluation to runtime.
05968  *
05969  * Side effects:
05970  *      Instructions are added to envPtr to execute the "global" command at
05971  *      runtime.
05972  *
05973  *----------------------------------------------------------------------
05974  */
05975 
05976 int
05977 TclCompileGlobalCmd(
05978     Tcl_Interp *interp,         /* Used for error reporting. */
05979     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
05980                                  * created by Tcl_ParseCommand. */
05981     Command *cmdPtr,            /* Points to defintion of command being
05982                                  * compiled. */
05983     CompileEnv *envPtr)         /* Holds resulting instructions. */
05984 {
05985     Tcl_Token *varTokenPtr;
05986     int localIndex, numWords, i;
05987     DefineLineInformation;      /* TIP #280 */
05988 
05989     numWords = parsePtr->numWords;
05990     if (numWords < 2) {
05991         return TCL_ERROR;
05992     }
05993 
05994     /*
05995      * 'global' has no effect outside of proc bodies; handle that at runtime
05996      */
05997 
05998     if (envPtr->procPtr == NULL) {
05999         return TCL_ERROR;
06000     }
06001 
06002     /*
06003      * Push the namespace
06004      */
06005 
06006     PushLiteral(envPtr, "::", 2);
06007 
06008     /*
06009      * Loop over the variables.
06010      */
06011 
06012     varTokenPtr = TokenAfter(parsePtr->tokenPtr);
06013     for(i=2; i<=numWords; varTokenPtr = TokenAfter(varTokenPtr),i++) {
06014         localIndex = IndexTailVarIfKnown(interp, varTokenPtr, envPtr);
06015 
06016         if(localIndex < 0) {
06017             return TCL_ERROR;
06018         }
06019 
06020         CompileWord(envPtr, varTokenPtr, interp, 1);
06021         TclEmitInstInt4(INST_NSUPVAR, localIndex, envPtr);
06022     }
06023 
06024     /*
06025      * Pop the namespace, and set the result to empty
06026      */
06027 
06028     TclEmitOpcode(INST_POP, envPtr);
06029     PushLiteral(envPtr, "", 0);
06030     return TCL_OK;
06031 }
06032 
06033 /*
06034  *----------------------------------------------------------------------
06035  *
06036  * TclCompileVariableCmd --
06037  *
06038  *      Procedure called to compile the "variable" command.
06039  *
06040  * Results:
06041  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
06042  *      evaluation to runtime.
06043  *
06044  * Side effects:
06045  *      Instructions are added to envPtr to execute the "variable" command at
06046  *      runtime.
06047  *
06048  *----------------------------------------------------------------------
06049  */
06050 
06051 int
06052 TclCompileVariableCmd(
06053     Tcl_Interp *interp,         /* Used for error reporting. */
06054     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
06055                                  * created by Tcl_ParseCommand. */
06056     Command *cmdPtr,            /* Points to defintion of command being
06057                                  * compiled. */
06058     CompileEnv *envPtr)         /* Holds resulting instructions. */
06059 {
06060     Tcl_Token *varTokenPtr, *valueTokenPtr;
06061     int localIndex, numWords, i;
06062     DefineLineInformation;      /* TIP #280 */
06063 
06064     numWords = parsePtr->numWords;
06065     if (numWords < 2) {
06066         return TCL_ERROR;
06067     }
06068 
06069     /*
06070      * Bail out if not compiling a proc body
06071      */
06072 
06073     if (envPtr->procPtr == NULL) {
06074         return TCL_ERROR;
06075     }
06076 
06077     /*
06078      * Loop over the (var, value) pairs.
06079      */
06080 
06081     valueTokenPtr = parsePtr->tokenPtr;
06082     for(i=2; i<=numWords; i+=2) {
06083         varTokenPtr = TokenAfter(valueTokenPtr);
06084         valueTokenPtr = TokenAfter(varTokenPtr);
06085 
06086         localIndex = IndexTailVarIfKnown(interp, varTokenPtr, envPtr);
06087 
06088         if(localIndex < 0) {
06089             return TCL_ERROR;
06090         }
06091 
06092         CompileWord(envPtr, varTokenPtr, interp, 1);
06093         TclEmitInstInt4(INST_VARIABLE, localIndex, envPtr);
06094 
06095         if (i != numWords) {
06096             /*
06097              * A value has been given: set the variable, pop the value
06098              */
06099 
06100             CompileWord(envPtr, valueTokenPtr, interp, 1);
06101             TclEmitInstInt4(INST_STORE_SCALAR4, localIndex, envPtr);
06102             TclEmitOpcode(INST_POP, envPtr);
06103         }
06104     }
06105 
06106     /*
06107      * Set the result to empty
06108      */
06109 
06110     PushLiteral(envPtr, "", 0);
06111     return TCL_OK;
06112 }
06113 
06114 /*
06115  *----------------------------------------------------------------------
06116  *
06117  * TclCompileEnsemble --
06118  *
06119  *      Procedure called to compile an ensemble command. Note that most
06120  *      ensembles are not compiled, since modifying a compiled ensemble causes
06121  *      a invalidation of all existing bytecode (expensive!) which is not
06122  *      normally warranted.
06123  *
06124  * Results:
06125  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
06126  *      evaluation to runtime.
06127  *
06128  * Side effects:
06129  *      Instructions are added to envPtr to execute the subcommands of the
06130  *      ensemble at runtime if a compile-time mapping is possible.
06131  *
06132  *----------------------------------------------------------------------
06133  */
06134 
06135 int
06136 TclCompileEnsemble(
06137     Tcl_Interp *interp,         /* Used for error reporting. */
06138     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
06139                                  * created by Tcl_ParseCommand. */
06140     Command *cmdPtr,            /* Points to defintion of command being
06141                                  * compiled. */
06142     CompileEnv *envPtr)         /* Holds resulting instructions. */
06143 {
06144     Tcl_Token *tokenPtr;
06145     Tcl_Obj *mapObj, *subcmdObj, *targetCmdObj, *listObj, **elems;
06146     Tcl_Command ensemble = (Tcl_Command) cmdPtr;
06147     Tcl_Parse synthetic;
06148     int len, numBytes, result, flags = 0, i;
06149     const char *word;
06150 
06151     if (parsePtr->numWords < 2) {
06152         return TCL_ERROR;
06153     }
06154 
06155     tokenPtr = TokenAfter(parsePtr->tokenPtr);
06156     if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
06157         /*
06158          * Too hard.
06159          */
06160 
06161         return TCL_ERROR;
06162     }
06163 
06164     word = tokenPtr[1].start;
06165     numBytes = tokenPtr[1].size;
06166 
06167     /*
06168      * There's a sporting chance we'll be able to compile this. But now we
06169      * must check properly. To do that, check that we're compiling an ensemble
06170      * that has a compilable command as its appropriate subcommand.
06171      */
06172 
06173     if (Tcl_GetEnsembleMappingDict(NULL, ensemble, &mapObj) != TCL_OK
06174             || mapObj == NULL) {
06175         /*
06176          * Either not an ensemble or a mapping isn't installed. Crud. Too hard
06177          * to proceed.
06178          */
06179 
06180         return TCL_ERROR;
06181     }
06182 
06183     /*
06184      * Next, get the flags. We need them on several code paths.
06185      */
06186 
06187     (void) Tcl_GetEnsembleFlags(NULL, ensemble, &flags);
06188 
06189     /*
06190      * Check to see if there's also a subcommand list; must check to see if
06191      * the subcommand we are calling is in that list if it exists, since that
06192      * list filters the entries in the map.
06193      */
06194 
06195     (void) Tcl_GetEnsembleSubcommandList(NULL, ensemble, &listObj);
06196     if (listObj != NULL) {
06197         int sclen;
06198         const char *str;
06199         Tcl_Obj *matchObj = NULL;
06200 
06201         if (Tcl_ListObjGetElements(NULL, listObj, &len, &elems) != TCL_OK) {
06202             return TCL_ERROR;
06203         }
06204         for (i=0 ; i<len ; i++) {
06205             str = Tcl_GetStringFromObj(elems[i], &sclen);
06206             if (sclen==numBytes && !memcmp(word, str, (unsigned) numBytes)) {
06207                 /*
06208                  * Exact match! Excellent!
06209                  */
06210 
06211                 result = Tcl_DictObjGet(NULL, mapObj,elems[i], &targetCmdObj);
06212                 if (result != TCL_OK || targetCmdObj == NULL) {
06213                     return TCL_ERROR;
06214                 }
06215                 goto doneMapLookup;
06216             }
06217 
06218             /*
06219              * Check to see if we've got a prefix match. A single prefix match
06220              * is fine, and allows us to refine our dictionary lookup, but
06221              * multiple prefix matches is a Bad Thing and will prevent us from
06222              * making progress. Note that we cannot do the lookup immediately
06223              * in the prefix case; might be another entry later in the list
06224              * that causes things to fail.
06225              */
06226 
06227             if ((flags & TCL_ENSEMBLE_PREFIX)
06228                     && strncmp(word, str, (unsigned) numBytes) == 0) {
06229                 if (matchObj != NULL) {
06230                     return TCL_ERROR;
06231                 }
06232                 matchObj = elems[i];
06233             }
06234         }
06235         if (matchObj != NULL) {
06236             result = Tcl_DictObjGet(NULL, mapObj, matchObj, &targetCmdObj);
06237             if (result != TCL_OK || targetCmdObj == NULL) {
06238                 return TCL_ERROR;
06239             }
06240             goto doneMapLookup;
06241         }
06242         return TCL_ERROR;
06243     } else {
06244         /*
06245          * No map, so check the dictionary directly.
06246          */
06247 
06248         TclNewStringObj(subcmdObj, word, numBytes);
06249         result = Tcl_DictObjGet(NULL, mapObj, subcmdObj, &targetCmdObj);
06250         TclDecrRefCount(subcmdObj);
06251         if (result == TCL_OK && targetCmdObj != NULL) {
06252             /*
06253              * Got it. Skip the fiddling around with prefixes.
06254              */
06255 
06256             goto doneMapLookup;
06257         }
06258 
06259         /*
06260          * We've not literally got a valid subcommand. But maybe we have a
06261          * prefix. Check if prefix matches are allowed.
06262          */
06263 
06264         if (flags & TCL_ENSEMBLE_PREFIX) {
06265             Tcl_DictSearch s;
06266             int done, matched;
06267             Tcl_Obj *tmpObj;
06268 
06269             /*
06270              * Iterate over the keys in the dictionary, checking to see if
06271              * we're a prefix.
06272              */
06273 
06274             Tcl_DictObjFirst(NULL,mapObj,&s,&subcmdObj,&tmpObj,&done);
06275             matched = 0;
06276             while (!done) {
06277                 if (strncmp(TclGetString(subcmdObj), word,
06278                         (unsigned) numBytes) == 0) {
06279                     if (matched++) {
06280                         /*
06281                          * Must have matched twice! Not unique, so no point
06282                          * looking further.
06283                          */
06284 
06285                         break;
06286                     }
06287                     targetCmdObj = tmpObj;
06288                 }
06289                 Tcl_DictObjNext(&s, &subcmdObj, &tmpObj, &done);
06290             }
06291             Tcl_DictObjDone(&s);
06292 
06293             /*
06294              * If we have anything other than a single match, we've failed the
06295              * unique prefix check.
06296              */
06297 
06298             if (matched != 1) {
06299                 return TCL_ERROR;
06300             }
06301         } else {
06302             return TCL_ERROR;
06303         }
06304     }
06305 
06306     /*
06307      * OK, we definitely map to something. But what?
06308      *
06309      * The command we map to is the first word out of the map element. Note
06310      * that we also reject dealing with multi-element rewrites if we are in a
06311      * safe interpreter, as there is otherwise a (highly gnarly!) way to make
06312      * Tcl crash open to exploit.
06313      */
06314 
06315   doneMapLookup:
06316     if (Tcl_ListObjGetElements(NULL, targetCmdObj, &len, &elems) != TCL_OK) {
06317         return TCL_ERROR;
06318     }
06319     if (len > 1 && Tcl_IsSafe(interp)) {
06320         return TCL_ERROR;
06321     }
06322     targetCmdObj = elems[0];
06323 
06324     Tcl_IncrRefCount(targetCmdObj);
06325     cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, targetCmdObj);
06326     TclDecrRefCount(targetCmdObj);
06327     if (cmdPtr == NULL || cmdPtr->compileProc == NULL) {
06328         /*
06329          * Maps to an undefined command or a command without a compiler.
06330          * Cannot compile.
06331          */
06332 
06333         return TCL_ERROR;
06334     }
06335 
06336     /*
06337      * Now we've done the mapping process, can now actually try to compile.
06338      * We do this by handing off to the subcommand's actual compiler. But to
06339      * do that, we have to perform some trickery to rewrite the arguments.
06340      */
06341 
06342     TclParseInit(interp, NULL, 0, &synthetic);
06343     synthetic.numWords = parsePtr->numWords - 2 + len;
06344     TclGrowParseTokenArray(&synthetic, 2*len);
06345     synthetic.numTokens = 2*len;
06346 
06347     /*
06348      * Now we have the space to work in, install something rewritten. Note
06349      * that we are here praying for all our might that none of these words are
06350      * a script; the error detection code will crash if that happens and there
06351      * is nothing we can do to avoid it!
06352      */
06353 
06354     for (i=0 ; i<len ; i++) {
06355         int sclen;
06356         const char *str = Tcl_GetStringFromObj(elems[i], &sclen);
06357 
06358         synthetic.tokenPtr[2*i].type = TCL_TOKEN_SIMPLE_WORD;
06359         synthetic.tokenPtr[2*i].start = str;
06360         synthetic.tokenPtr[2*i].size = sclen;
06361         synthetic.tokenPtr[2*i].numComponents = 1;
06362 
06363         synthetic.tokenPtr[2*i+1].type = TCL_TOKEN_TEXT;
06364         synthetic.tokenPtr[2*i+1].start = str;
06365         synthetic.tokenPtr[2*i+1].size = sclen;
06366         synthetic.tokenPtr[2*i+1].numComponents = 0;
06367     }
06368 
06369     /*
06370      * Copy over the real argument tokens.
06371      */
06372 
06373     for (i=len; i<synthetic.numWords; i++) {
06374         int toCopy;
06375         tokenPtr = TokenAfter(tokenPtr);
06376         toCopy = tokenPtr->numComponents + 1;
06377         TclGrowParseTokenArray(&synthetic, toCopy);
06378         memcpy(synthetic.tokenPtr + synthetic.numTokens, tokenPtr,
06379                 sizeof(Tcl_Token) * toCopy);
06380         synthetic.numTokens += toCopy;
06381     }
06382 
06383     /*
06384      * Hand off compilation to the subcommand compiler. At last!
06385      */
06386 
06387     result = cmdPtr->compileProc(interp, &synthetic, cmdPtr, envPtr);
06388 
06389     /*
06390      * Clean up if necessary.
06391      */
06392 
06393     Tcl_FreeParse(&synthetic);
06394     return result;
06395 }
06396 
06397 /*
06398  *----------------------------------------------------------------------
06399  *
06400  * TclCompileInfoExistsCmd --
06401  *
06402  *      Procedure called to compile the "info exists" subcommand.
06403  *
06404  * Results:
06405  *      Returns TCL_OK for a successful compile. Returns TCL_ERROR to defer
06406  *      evaluation to runtime.
06407  *
06408  * Side effects:
06409  *      Instructions are added to envPtr to execute the "info exists"
06410  *      subcommand at runtime.
06411  *
06412  *----------------------------------------------------------------------
06413  */
06414 
06415 int
06416 TclCompileInfoExistsCmd(
06417     Tcl_Interp *interp,         /* Used for error reporting. */
06418     Tcl_Parse *parsePtr,        /* Points to a parse structure for the command
06419                                  * created by Tcl_ParseCommand. */
06420     Command *cmdPtr,            /* Points to defintion of command being
06421                                  * compiled. */
06422     CompileEnv *envPtr)         /* Holds resulting instructions. */
06423 {
06424     Tcl_Token *tokenPtr;
06425     int isScalar, simpleVarName, localIndex;
06426     DefineLineInformation;      /* TIP #280 */
06427 
06428     if (parsePtr->numWords != 2) {
06429         return TCL_ERROR;
06430     }
06431 
06432     /*
06433      * Decide if we can use a frame slot for the var/array name or if we need
06434      * to emit code to compute and push the name at runtime. We use a frame
06435      * slot (entry in the array of local vars) if we are compiling a procedure
06436      * body and if the name is simple text that does not include namespace
06437      * qualifiers.
06438      */
06439 
06440     tokenPtr = TokenAfter(parsePtr->tokenPtr);
06441     PushVarName(interp, tokenPtr, envPtr, TCL_CREATE_VAR, &localIndex,
06442             &simpleVarName, &isScalar, mapPtr->loc[eclIndex].line[1]);
06443 
06444     /*
06445      * Emit instruction to check the variable for existence.
06446      */
06447 
06448     if (simpleVarName) {
06449         if (isScalar) {
06450             if (localIndex < 0) {
06451                 TclEmitOpcode(INST_EXIST_STK, envPtr);
06452             } else {
06453                 TclEmitInstInt4(INST_EXIST_SCALAR, localIndex, envPtr);
06454             }
06455         } else {
06456             if (localIndex < 0) {
06457                 TclEmitOpcode(INST_EXIST_ARRAY_STK, envPtr);
06458             } else {
06459                 TclEmitInstInt4(INST_EXIST_ARRAY, localIndex, envPtr);
06460             }
06461         }
06462     } else {
06463         TclEmitOpcode(INST_EXIST_STK, envPtr);
06464     }
06465 
06466     return TCL_OK;
06467 }
06468 
06469 /*
06470  * Local Variables:
06471  * mode: c
06472  * c-basic-offset: 4
06473  * fill-column: 78
06474  * End:
06475  */



Generated on Wed Mar 12 12:18:13 2008 by  doxygen 1.5.1