tclCompCmds.cGo 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 ![]() |