/[dtapublic]/projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclexecute.c
ViewVC logotype

Contents of /projs/trunk/shared_source/c_tcl_base_7_5_w_mods/tclexecute.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 44 - (show annotations) (download)
Fri Oct 14 02:09:58 2016 UTC (7 years, 9 months ago) by dashley
File MIME type: text/plain
File size: 156893 byte(s)
Rename for reorganization.
1 /* $Header: /cvsroot/esrg/sfesrg/esrgpcpj/shared/tcl_base/tclexecute.c,v 1.1.1.1 2001/06/13 04:38:49 dtashley Exp $ */
2
3 /*
4 * tclExecute.c --
5 *
6 * This file contains procedures that execute byte-compiled Tcl
7 * commands.
8 *
9 * Copyright (c) 1996-1997 Sun Microsystems, Inc.
10 *
11 * See the file "license.terms" for information on usage and redistribution
12 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
13 *
14 * RCS: @(#) $Id: tclexecute.c,v 1.1.1.1 2001/06/13 04:38:49 dtashley Exp $
15 */
16
17 #include "tclInt.h"
18 #include "tclCompile.h"
19
20 #ifdef NO_FLOAT_H
21 # include "../compat/float.h"
22 #else
23 # include <float.h>
24 #endif
25 #ifndef TCL_NO_MATH
26 #include "tclMath.h"
27 #endif
28
29 /*
30 * The stuff below is a bit of a hack so that this file can be used
31 * in environments that include no UNIX, i.e. no errno. Just define
32 * errno here.
33 */
34
35 #ifndef TCL_GENERIC_ONLY
36 #include "tclPort.h"
37 #else
38 #define NO_ERRNO_H
39 #endif
40
41 #ifdef NO_ERRNO_H
42 int errno;
43 #define EDOM 33
44 #define ERANGE 34
45 #endif
46
47 /*
48 * Boolean flag indicating whether the Tcl bytecode interpreter has been
49 * initialized.
50 */
51
52 static int execInitialized = 0;
53 TCL_DECLARE_MUTEX(execMutex)
54
55 /*
56 * Variable that controls whether execution tracing is enabled and, if so,
57 * what level of tracing is desired:
58 * 0: no execution tracing
59 * 1: trace invocations of Tcl procs only
60 * 2: trace invocations of all (not compiled away) commands
61 * 3: display each instruction executed
62 * This variable is linked to the Tcl variable "tcl_traceExec".
63 */
64
65 int tclTraceExec = 0;
66
67 typedef struct ThreadSpecificData {
68 /*
69 * The following global variable is use to signal matherr that Tcl
70 * is responsible for the arithmetic, so errors can be handled in a
71 * fashion appropriate for Tcl. Zero means no Tcl math is in
72 * progress; non-zero means Tcl is doing math.
73 */
74
75 int mathInProgress;
76
77 } ThreadSpecificData;
78
79 static Tcl_ThreadDataKey dataKey;
80
81 /*
82 * The variable below serves no useful purpose except to generate
83 * a reference to matherr, so that the Tcl version of matherr is
84 * linked in rather than the system version. Without this reference
85 * the need for matherr won't be discovered during linking until after
86 * libtcl.a has been processed, so Tcl's version won't be used.
87 */
88
89 #ifdef NEED_MATHERR
90 extern int matherr();
91 int (*tclMatherrPtr)() = matherr;
92 #endif
93
94 /*
95 * Mapping from expression instruction opcodes to strings; used for error
96 * messages. Note that these entries must match the order and number of the
97 * expression opcodes (e.g., INST_LOR) in tclCompile.h.
98 */
99
100 static char *operatorStrings[] = {
101 "||", "&&", "|", "^", "&", "==", "!=", "<", ">", "<=", ">=", "<<", ">>",
102 "+", "-", "*", "/", "%", "+", "-", "~", "!",
103 "BUILTIN FUNCTION", "FUNCTION"
104 };
105
106 /*
107 * Mapping from Tcl result codes to strings; used for error and debugging
108 * messages.
109 */
110
111 #ifdef TCL_COMPILE_DEBUG
112 static char *resultStrings[] = {
113 "TCL_OK", "TCL_ERROR", "TCL_RETURN", "TCL_BREAK", "TCL_CONTINUE"
114 };
115 #endif
116
117 /*
118 * Macros for testing floating-point values for certain special cases. Test
119 * for not-a-number by comparing a value against itself; test for infinity
120 * by comparing against the largest floating-point value.
121 */
122
123 #define IS_NAN(v) ((v) != (v))
124 #ifdef DBL_MAX
125 # define IS_INF(v) (((v) > DBL_MAX) || ((v) < -DBL_MAX))
126 #else
127 # define IS_INF(v) 0
128 #endif
129
130 /*
131 * Macro to adjust the program counter and restart the instruction execution
132 * loop after each instruction is executed.
133 */
134
135 #define ADJUST_PC(instBytes) \
136 pc += (instBytes); \
137 continue
138
139 /*
140 * Macros used to cache often-referenced Tcl evaluation stack information
141 * in local variables. Note that a DECACHE_STACK_INFO()-CACHE_STACK_INFO()
142 * pair must surround any call inside TclExecuteByteCode (and a few other
143 * procedures that use this scheme) that could result in a recursive call
144 * to TclExecuteByteCode.
145 */
146
147 #define CACHE_STACK_INFO() \
148 stackPtr = eePtr->stackPtr; \
149 stackTop = eePtr->stackTop
150
151 #define DECACHE_STACK_INFO() \
152 eePtr->stackTop = stackTop
153
154 /*
155 * Macros used to access items on the Tcl evaluation stack. PUSH_OBJECT
156 * increments the object's ref count since it makes the stack have another
157 * reference pointing to the object. However, POP_OBJECT does not decrement
158 * the ref count. This is because the stack may hold the only reference to
159 * the object, so the object would be destroyed if its ref count were
160 * decremented before the caller had a chance to, e.g., store it in a
161 * variable. It is the caller's responsibility to decrement the ref count
162 * when it is finished with an object.
163 *
164 * WARNING! It is essential that objPtr only appear once in the PUSH_OBJECT
165 * macro. The actual parameter might be an expression with side effects,
166 * and this ensures that it will be executed only once.
167 */
168
169 #define PUSH_OBJECT(objPtr) \
170 Tcl_IncrRefCount(stackPtr[++stackTop] = (objPtr))
171
172 #define POP_OBJECT() \
173 (stackPtr[stackTop--])
174
175 /*
176 * Macros used to trace instruction execution. The macros TRACE,
177 * TRACE_WITH_OBJ, and O2S are only used inside TclExecuteByteCode.
178 * O2S is only used in TRACE* calls to get a string from an object.
179 */
180
181 #ifdef TCL_COMPILE_DEBUG
182 #define TRACE(a) \
183 if (traceInstructions) { \
184 fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, stackTop, \
185 (unsigned int)(pc - codePtr->codeStart), \
186 GetOpcodeName(pc)); \
187 printf a; \
188 }
189 #define TRACE_WITH_OBJ(a, objPtr) \
190 if (traceInstructions) { \
191 fprintf(stdout, "%2d: %2d (%u) %s ", iPtr->numLevels, stackTop, \
192 (unsigned int)(pc - codePtr->codeStart), \
193 GetOpcodeName(pc)); \
194 printf a; \
195 TclPrintObject(stdout, (objPtr), 30); \
196 fprintf(stdout, "\n"); \
197 }
198 #define O2S(objPtr) \
199 Tcl_GetString(objPtr)
200 #else
201 #define TRACE(a)
202 #define TRACE_WITH_OBJ(a, objPtr)
203 #define O2S(objPtr)
204 #endif /* TCL_COMPILE_DEBUG */
205
206 /*
207 * Declarations for local procedures to this file:
208 */
209
210 static void CallTraceProcedure _ANSI_ARGS_((Tcl_Interp *interp,
211 Trace *tracePtr, Command *cmdPtr,
212 char *command, int numChars,
213 int objc, Tcl_Obj *objv[]));
214 static void DupCmdNameInternalRep _ANSI_ARGS_((Tcl_Obj *objPtr,
215 Tcl_Obj *copyPtr));
216 static int ExprAbsFunc _ANSI_ARGS_((Tcl_Interp *interp,
217 ExecEnv *eePtr, ClientData clientData));
218 static int ExprBinaryFunc _ANSI_ARGS_((Tcl_Interp *interp,
219 ExecEnv *eePtr, ClientData clientData));
220 static int ExprCallMathFunc _ANSI_ARGS_((Tcl_Interp *interp,
221 ExecEnv *eePtr, int objc, Tcl_Obj **objv));
222 static int ExprDoubleFunc _ANSI_ARGS_((Tcl_Interp *interp,
223 ExecEnv *eePtr, ClientData clientData));
224 static int ExprIntFunc _ANSI_ARGS_((Tcl_Interp *interp,
225 ExecEnv *eePtr, ClientData clientData));
226 static int ExprRandFunc _ANSI_ARGS_((Tcl_Interp *interp,
227 ExecEnv *eePtr, ClientData clientData));
228 static int ExprRoundFunc _ANSI_ARGS_((Tcl_Interp *interp,
229 ExecEnv *eePtr, ClientData clientData));
230 static int ExprSrandFunc _ANSI_ARGS_((Tcl_Interp *interp,
231 ExecEnv *eePtr, ClientData clientData));
232 static int ExprUnaryFunc _ANSI_ARGS_((Tcl_Interp *interp,
233 ExecEnv *eePtr, ClientData clientData));
234 #ifdef TCL_COMPILE_STATS
235 static int EvalStatsCmd _ANSI_ARGS_((ClientData clientData,
236 Tcl_Interp *interp, int argc, char **argv));
237 #endif
238 static void FreeCmdNameInternalRep _ANSI_ARGS_((
239 Tcl_Obj *objPtr));
240 #ifdef TCL_COMPILE_DEBUG
241 static char * GetOpcodeName _ANSI_ARGS_((unsigned char *pc));
242 #endif
243 static ExceptionRange * GetExceptRangeForPc _ANSI_ARGS_((unsigned char *pc,
244 int catchOnly, ByteCode* codePtr));
245 static char * GetSrcInfoForPc _ANSI_ARGS_((unsigned char *pc,
246 ByteCode* codePtr, int *lengthPtr));
247 static void GrowEvaluationStack _ANSI_ARGS_((ExecEnv *eePtr));
248 static void IllegalExprOperandType _ANSI_ARGS_((
249 Tcl_Interp *interp, unsigned char *pc,
250 Tcl_Obj *opndPtr));
251 static void InitByteCodeExecution _ANSI_ARGS_((
252 Tcl_Interp *interp));
253 #ifdef TCL_COMPILE_DEBUG
254 static void PrintByteCodeInfo _ANSI_ARGS_((ByteCode *codePtr));
255 #endif
256 static int SetCmdNameFromAny _ANSI_ARGS_((Tcl_Interp *interp,
257 Tcl_Obj *objPtr));
258 #ifdef TCL_COMPILE_DEBUG
259 static char * StringForResultCode _ANSI_ARGS_((int result));
260 static void ValidatePcAndStackTop _ANSI_ARGS_((
261 ByteCode *codePtr, unsigned char *pc,
262 int stackTop, int stackLowerBound,
263 int stackUpperBound));
264 #endif
265 static int VerifyExprObjType _ANSI_ARGS_((Tcl_Interp *interp,
266 Tcl_Obj *objPtr));
267
268 /*
269 * Table describing the built-in math functions. Entries in this table are
270 * indexed by the values of the INST_CALL_BUILTIN_FUNC instruction's
271 * operand byte.
272 */
273
274 BuiltinFunc builtinFuncTable[] = {
275 #ifndef TCL_NO_MATH
276 {"acos", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) acos},
277 {"asin", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) asin},
278 {"atan", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) atan},
279 {"atan2", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) atan2},
280 {"ceil", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) ceil},
281 {"cos", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) cos},
282 {"cosh", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) cosh},
283 {"exp", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) exp},
284 {"floor", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) floor},
285 {"fmod", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) fmod},
286 {"hypot", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) hypot},
287 {"log", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) log},
288 {"log10", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) log10},
289 {"pow", 2, {TCL_DOUBLE, TCL_DOUBLE}, ExprBinaryFunc, (ClientData) pow},
290 {"sin", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) sin},
291 {"sinh", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) sinh},
292 {"sqrt", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) sqrt},
293 {"tan", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) tan},
294 {"tanh", 1, {TCL_DOUBLE}, ExprUnaryFunc, (ClientData) tanh},
295 #endif
296 {"abs", 1, {TCL_EITHER}, ExprAbsFunc, 0},
297 {"double", 1, {TCL_EITHER}, ExprDoubleFunc, 0},
298 {"int", 1, {TCL_EITHER}, ExprIntFunc, 0},
299 {"rand", 0, {TCL_EITHER}, ExprRandFunc, 0}, /* NOTE: rand takes no args. */
300 {"round", 1, {TCL_EITHER}, ExprRoundFunc, 0},
301 {"srand", 1, {TCL_INT}, ExprSrandFunc, 0},
302 {0},
303 };
304
305 /*
306 * The structure below defines the command name Tcl object type by means of
307 * procedures that can be invoked by generic object code. Objects of this
308 * type cache the Command pointer that results from looking up command names
309 * in the command hashtable. Such objects appear as the zeroth ("command
310 * name") argument in a Tcl command.
311 */
312
313 Tcl_ObjType tclCmdNameType = {
314 "cmdName", /* name */
315 FreeCmdNameInternalRep, /* freeIntRepProc */
316 DupCmdNameInternalRep, /* dupIntRepProc */
317 (Tcl_UpdateStringProc *) NULL, /* updateStringProc */
318 SetCmdNameFromAny /* setFromAnyProc */
319 };
320
321 /*
322 *----------------------------------------------------------------------
323 *
324 * InitByteCodeExecution --
325 *
326 * This procedure is called once to initialize the Tcl bytecode
327 * interpreter.
328 *
329 * Results:
330 * None.
331 *
332 * Side effects:
333 * This procedure initializes the array of instruction names. If
334 * compiling with the TCL_COMPILE_STATS flag, it initializes the
335 * array that counts the executions of each instruction and it
336 * creates the "evalstats" command. It also registers the command name
337 * Tcl_ObjType. It also establishes the link between the Tcl
338 * "tcl_traceExec" and C "tclTraceExec" variables.
339 *
340 *----------------------------------------------------------------------
341 */
342
343 static void
344 InitByteCodeExecution(interp)
345 Tcl_Interp *interp; /* Interpreter for which the Tcl variable
346 * "tcl_traceExec" is linked to control
347 * instruction tracing. */
348 {
349 Tcl_RegisterObjType(&tclCmdNameType);
350 if (Tcl_LinkVar(interp, "tcl_traceExec", (char *) &tclTraceExec,
351 TCL_LINK_INT) != TCL_OK) {
352 panic("InitByteCodeExecution: can't create link for tcl_traceExec variable");
353 }
354
355 #ifdef TCL_COMPILE_STATS
356 Tcl_CreateCommand(interp, "evalstats", EvalStatsCmd,
357 (ClientData) NULL, (Tcl_CmdDeleteProc *) NULL);
358 #endif /* TCL_COMPILE_STATS */
359 }
360
361 /*
362 *----------------------------------------------------------------------
363 *
364 * TclCreateExecEnv --
365 *
366 * This procedure creates a new execution environment for Tcl bytecode
367 * execution. An ExecEnv points to a Tcl evaluation stack. An ExecEnv
368 * is typically created once for each Tcl interpreter (Interp
369 * structure) and recursively passed to TclExecuteByteCode to execute
370 * ByteCode sequences for nested commands.
371 *
372 * Results:
373 * A newly allocated ExecEnv is returned. This points to an empty
374 * evaluation stack of the standard initial size.
375 *
376 * Side effects:
377 * The bytecode interpreter is also initialized here, as this
378 * procedure will be called before any call to TclExecuteByteCode.
379 *
380 *----------------------------------------------------------------------
381 */
382
383 #define TCL_STACK_INITIAL_SIZE 2000
384
385 ExecEnv *
386 TclCreateExecEnv(interp)
387 Tcl_Interp *interp; /* Interpreter for which the execution
388 * environment is being created. */
389 {
390 ExecEnv *eePtr = (ExecEnv *) ckalloc(sizeof(ExecEnv));
391
392 eePtr->stackPtr = (Tcl_Obj **)
393 ckalloc((unsigned) (TCL_STACK_INITIAL_SIZE * sizeof(Tcl_Obj *)));
394 eePtr->stackTop = -1;
395 eePtr->stackEnd = (TCL_STACK_INITIAL_SIZE - 1);
396
397 Tcl_MutexLock(&execMutex);
398 if (!execInitialized) {
399 TclInitAuxDataTypeTable();
400 InitByteCodeExecution(interp);
401 execInitialized = 1;
402 }
403 Tcl_MutexUnlock(&execMutex);
404
405 return eePtr;
406 }
407 #undef TCL_STACK_INITIAL_SIZE
408
409 /*
410 *----------------------------------------------------------------------
411 *
412 * TclDeleteExecEnv --
413 *
414 * Frees the storage for an ExecEnv.
415 *
416 * Results:
417 * None.
418 *
419 * Side effects:
420 * Storage for an ExecEnv and its contained storage (e.g. the
421 * evaluation stack) is freed.
422 *
423 *----------------------------------------------------------------------
424 */
425
426 void
427 TclDeleteExecEnv(eePtr)
428 ExecEnv *eePtr; /* Execution environment to free. */
429 {
430 ckfree((char *) eePtr->stackPtr);
431 ckfree((char *) eePtr);
432 }
433
434 /*
435 *----------------------------------------------------------------------
436 *
437 * TclFinalizeExecution --
438 *
439 * Finalizes the execution environment setup so that it can be
440 * later reinitialized.
441 *
442 * Results:
443 * None.
444 *
445 * Side effects:
446 * After this call, the next time TclCreateExecEnv will be called
447 * it will call InitByteCodeExecution.
448 *
449 *----------------------------------------------------------------------
450 */
451
452 void
453 TclFinalizeExecution()
454 {
455 Tcl_MutexLock(&execMutex);
456 execInitialized = 0;
457 Tcl_MutexUnlock(&execMutex);
458 TclFinalizeAuxDataTypeTable();
459 }
460
461 /*
462 *----------------------------------------------------------------------
463 *
464 * GrowEvaluationStack --
465 *
466 * This procedure grows a Tcl evaluation stack stored in an ExecEnv.
467 *
468 * Results:
469 * None.
470 *
471 * Side effects:
472 * The size of the evaluation stack is doubled.
473 *
474 *----------------------------------------------------------------------
475 */
476
477 static void
478 GrowEvaluationStack(eePtr)
479 register ExecEnv *eePtr; /* Points to the ExecEnv with an evaluation
480 * stack to enlarge. */
481 {
482 /*
483 * The current Tcl stack elements are stored from eePtr->stackPtr[0]
484 * to eePtr->stackPtr[eePtr->stackEnd] (inclusive).
485 */
486
487 int currElems = (eePtr->stackEnd + 1);
488 int newElems = 2*currElems;
489 int currBytes = currElems * sizeof(Tcl_Obj *);
490 int newBytes = 2*currBytes;
491 Tcl_Obj **newStackPtr = (Tcl_Obj **) ckalloc((unsigned) newBytes);
492
493 /*
494 * Copy the existing stack items to the new stack space, free the old
495 * storage if appropriate, and mark new space as malloc'ed.
496 */
497
498 memcpy((VOID *) newStackPtr, (VOID *) eePtr->stackPtr,
499 (size_t) currBytes);
500 ckfree((char *) eePtr->stackPtr);
501 eePtr->stackPtr = newStackPtr;
502 eePtr->stackEnd = (newElems - 1); /* i.e. index of last usable item */
503 }
504
505 /*
506 *----------------------------------------------------------------------
507 *
508 * TclExecuteByteCode --
509 *
510 * This procedure executes the instructions of a ByteCode structure.
511 * It returns when a "done" instruction is executed or an error occurs.
512 *
513 * Results:
514 * The return value is one of the return codes defined in tcl.h
515 * (such as TCL_OK), and interp->objResultPtr refers to a Tcl object
516 * that either contains the result of executing the code or an
517 * error message.
518 *
519 * Side effects:
520 * Almost certainly, depending on the ByteCode's instructions.
521 *
522 *----------------------------------------------------------------------
523 */
524
525 int
526 TclExecuteByteCode(interp, codePtr)
527 Tcl_Interp *interp; /* Token for command interpreter. */
528 ByteCode *codePtr; /* The bytecode sequence to interpret. */
529 {
530 Interp *iPtr = (Interp *) interp;
531 ExecEnv *eePtr = iPtr->execEnvPtr;
532 /* Points to the execution environment. */
533 register Tcl_Obj **stackPtr = eePtr->stackPtr;
534 /* Cached evaluation stack base pointer. */
535 register int stackTop = eePtr->stackTop;
536 /* Cached top index of evaluation stack. */
537 register unsigned char *pc = codePtr->codeStart;
538 /* The current program counter. */
539 int opnd; /* Current instruction's operand byte. */
540 int pcAdjustment; /* Hold pc adjustment after instruction. */
541 int initStackTop = stackTop;/* Stack top at start of execution. */
542 ExceptionRange *rangePtr; /* Points to closest loop or catch exception
543 * range enclosing the pc. Used by various
544 * instructions and processCatch to
545 * process break, continue, and errors. */
546 int result = TCL_OK; /* Return code returned after execution. */
547 int traceInstructions = (tclTraceExec == 3);
548 Tcl_Obj *valuePtr, *value2Ptr, *objPtr;
549 char *bytes;
550 int length;
551 long i;
552
553 /*
554 * This procedure uses a stack to hold information about catch commands.
555 * This information is the current operand stack top when starting to
556 * execute the code for each catch command. It starts out with stack-
557 * allocated space but uses dynamically-allocated storage if needed.
558 */
559
560 #define STATIC_CATCH_STACK_SIZE 4
561 int (catchStackStorage[STATIC_CATCH_STACK_SIZE]);
562 int *catchStackPtr = catchStackStorage;
563 int catchTop = -1;
564
565 #ifdef TCL_COMPILE_DEBUG
566 if (tclTraceExec >= 2) {
567 PrintByteCodeInfo(codePtr);
568 fprintf(stdout, " Starting stack top=%d\n", eePtr->stackTop);
569 fflush(stdout);
570 }
571 #endif
572
573 #ifdef TCL_COMPILE_STATS
574 iPtr->stats.numExecutions++;
575 #endif
576
577 /*
578 * Make sure the catch stack is large enough to hold the maximum number
579 * of catch commands that could ever be executing at the same time. This
580 * will be no more than the exception range array's depth.
581 */
582
583 if (codePtr->maxExceptDepth > STATIC_CATCH_STACK_SIZE) {
584 catchStackPtr = (int *)
585 ckalloc(codePtr->maxExceptDepth * sizeof(int));
586 }
587
588 /*
589 * Make sure the stack has enough room to execute this ByteCode.
590 */
591
592 while ((stackTop + codePtr->maxStackDepth) > eePtr->stackEnd) {
593 GrowEvaluationStack(eePtr);
594 stackPtr = eePtr->stackPtr;
595 }
596
597 /*
598 * Loop executing instructions until a "done" instruction, a TCL_RETURN,
599 * or some error.
600 */
601
602 for (;;) {
603 #ifdef TCL_COMPILE_DEBUG
604 ValidatePcAndStackTop(codePtr, pc, stackTop, initStackTop,
605 eePtr->stackEnd);
606 #else /* not TCL_COMPILE_DEBUG */
607 if (traceInstructions) {
608 fprintf(stdout, "%2d: %2d ", iPtr->numLevels, stackTop);
609 TclPrintInstruction(codePtr, pc);
610 fflush(stdout);
611 }
612 #endif /* TCL_COMPILE_DEBUG */
613
614 #ifdef TCL_COMPILE_STATS
615 iPtr->stats.instructionCount[*pc]++;
616 #endif
617 switch (*pc) {
618 case INST_DONE:
619 /*
620 * Pop the topmost object from the stack, set the interpreter's
621 * object result to point to it, and return.
622 */
623 valuePtr = POP_OBJECT();
624 Tcl_SetObjResult(interp, valuePtr);
625 TclDecrRefCount(valuePtr);
626 if (stackTop != initStackTop) {
627 fprintf(stderr, "\nTclExecuteByteCode: done instruction at pc %u: stack top %d != entry stack top %d\n",
628 (unsigned int)(pc - codePtr->codeStart),
629 (unsigned int) stackTop,
630 (unsigned int) initStackTop);
631 panic("TclExecuteByteCode execution failure: end stack top != start stack top");
632 }
633 TRACE_WITH_OBJ(("=> return code=%d, result=", result),
634 iPtr->objResultPtr);
635 #ifdef TCL_COMPILE_DEBUG
636 if (traceInstructions) {
637 fprintf(stdout, "\n");
638 }
639 #endif
640 goto done;
641
642 case INST_PUSH1:
643 #ifdef TCL_COMPILE_DEBUG
644 valuePtr = codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)];
645 PUSH_OBJECT(valuePtr);
646 TRACE_WITH_OBJ(("%u => ", TclGetInt1AtPtr(pc+1)), valuePtr);
647 #else
648 PUSH_OBJECT(codePtr->objArrayPtr[TclGetUInt1AtPtr(pc+1)]);
649 #endif /* TCL_COMPILE_DEBUG */
650 ADJUST_PC(2);
651
652 case INST_PUSH4:
653 valuePtr = codePtr->objArrayPtr[TclGetUInt4AtPtr(pc+1)];
654 PUSH_OBJECT(valuePtr);
655 TRACE_WITH_OBJ(("%u => ", TclGetUInt4AtPtr(pc+1)), valuePtr);
656 ADJUST_PC(5);
657
658 case INST_POP:
659 valuePtr = POP_OBJECT();
660 TRACE_WITH_OBJ(("=> discarding "), valuePtr);
661 TclDecrRefCount(valuePtr); /* finished with pop'ed object. */
662 ADJUST_PC(1);
663
664 case INST_DUP:
665 valuePtr = stackPtr[stackTop];
666 PUSH_OBJECT(Tcl_DuplicateObj(valuePtr));
667 TRACE_WITH_OBJ(("=> "), valuePtr);
668 ADJUST_PC(1);
669
670 case INST_CONCAT1:
671 opnd = TclGetUInt1AtPtr(pc+1);
672 {
673 Tcl_Obj *concatObjPtr;
674 int totalLen = 0;
675
676 /*
677 * Concatenate strings (with no separators) from the top
678 * opnd items on the stack starting with the deepest item.
679 * First, determine how many characters are needed.
680 */
681
682 for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
683 bytes = Tcl_GetStringFromObj(stackPtr[i], &length);
684 if (bytes != NULL) {
685 totalLen += length;
686 }
687 }
688
689 /*
690 * Initialize the new append string object by appending the
691 * strings of the opnd stack objects. Also pop the objects.
692 */
693
694 TclNewObj(concatObjPtr);
695 if (totalLen > 0) {
696 char *p = (char *) ckalloc((unsigned) (totalLen + 1));
697 concatObjPtr->bytes = p;
698 concatObjPtr->length = totalLen;
699 for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
700 valuePtr = stackPtr[i];
701 bytes = Tcl_GetStringFromObj(valuePtr, &length);
702 if (bytes != NULL) {
703 memcpy((VOID *) p, (VOID *) bytes,
704 (size_t) length);
705 p += length;
706 }
707 TclDecrRefCount(valuePtr);
708 }
709 *p = '\0';
710 } else {
711 for (i = (stackTop - (opnd-1)); i <= stackTop; i++) {
712 Tcl_DecrRefCount(stackPtr[i]);
713 }
714 }
715 stackTop -= opnd;
716
717 PUSH_OBJECT(concatObjPtr);
718 TRACE_WITH_OBJ(("%u => ", opnd), concatObjPtr);
719 ADJUST_PC(2);
720 }
721
722 case INST_INVOKE_STK4:
723 opnd = TclGetUInt4AtPtr(pc+1);
724 pcAdjustment = 5;
725 goto doInvocation;
726
727 case INST_INVOKE_STK1:
728 opnd = TclGetUInt1AtPtr(pc+1);
729 pcAdjustment = 2;
730
731 doInvocation:
732 {
733 int objc = opnd; /* The number of arguments. */
734 Tcl_Obj **objv; /* The array of argument objects. */
735 Command *cmdPtr; /* Points to command's Command struct. */
736 int newPcOffset; /* New inst offset for break, continue. */
737 #ifdef TCL_COMPILE_DEBUG
738 int isUnknownCmd = 0;
739 char cmdNameBuf[21];
740 #endif /* TCL_COMPILE_DEBUG */
741
742 /*
743 * If the interpreter was deleted, return an error.
744 */
745
746 if (iPtr->flags & DELETED) {
747 Tcl_ResetResult(interp);
748 Tcl_AppendToObj(Tcl_GetObjResult(interp),
749 "attempt to call eval in deleted interpreter", -1);
750 Tcl_SetErrorCode(interp, "CORE", "IDELETE",
751 "attempt to call eval in deleted interpreter",
752 (char *) NULL);
753 result = TCL_ERROR;
754 goto checkForCatch;
755 }
756
757 /*
758 * Find the procedure to execute this command. If the
759 * command is not found, handle it with the "unknown" proc.
760 */
761
762 objv = &(stackPtr[stackTop - (objc-1)]);
763 cmdPtr = (Command *) Tcl_GetCommandFromObj(interp, objv[0]);
764 if (cmdPtr == NULL) {
765 cmdPtr = (Command *) Tcl_FindCommand(interp, "unknown",
766 (Tcl_Namespace *) NULL, TCL_GLOBAL_ONLY);
767 if (cmdPtr == NULL) {
768 Tcl_ResetResult(interp);
769 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
770 "invalid command name \"",
771 Tcl_GetString(objv[0]), "\"",
772 (char *) NULL);
773 TRACE(("%u => unknown proc not found: ", objc));
774 result = TCL_ERROR;
775 goto checkForCatch;
776 }
777 #ifdef TCL_COMPILE_DEBUG
778 isUnknownCmd = 1;
779 #endif /*TCL_COMPILE_DEBUG*/
780 stackTop++; /* need room for new inserted objv[0] */
781 for (i = objc-1; i >= 0; i--) {
782 objv[i+1] = objv[i];
783 }
784 objc++;
785 objv[0] = Tcl_NewStringObj("unknown", -1);
786 Tcl_IncrRefCount(objv[0]);
787 }
788
789 /*
790 * Call any trace procedures.
791 */
792
793 if (iPtr->tracePtr != NULL) {
794 Trace *tracePtr, *nextTracePtr;
795
796 for (tracePtr = iPtr->tracePtr; tracePtr != NULL;
797 tracePtr = nextTracePtr) {
798 nextTracePtr = tracePtr->nextPtr;
799 if (iPtr->numLevels <= tracePtr->level) {
800 int numChars;
801 char *cmd = GetSrcInfoForPc(pc, codePtr,
802 &numChars);
803 if (cmd != NULL) {
804 DECACHE_STACK_INFO();
805 CallTraceProcedure(interp, tracePtr, cmdPtr,
806 cmd, numChars, objc, objv);
807 CACHE_STACK_INFO();
808 }
809 }
810 }
811 }
812
813 /*
814 * Finally, invoke the command's Tcl_ObjCmdProc. First reset
815 * the interpreter's string and object results to their
816 * default empty values since they could have gotten changed
817 * by earlier invocations.
818 */
819
820 Tcl_ResetResult(interp);
821 if (tclTraceExec >= 2) {
822 #ifdef TCL_COMPILE_DEBUG
823 if (traceInstructions) {
824 strncpy(cmdNameBuf, Tcl_GetString(objv[0]), 20);
825 TRACE(("%u => call ", (isUnknownCmd? objc-1:objc)));
826 } else {
827 fprintf(stdout, "%d: (%u) invoking ",
828 iPtr->numLevels,
829 (unsigned int)(pc - codePtr->codeStart));
830 }
831 for (i = 0; i < objc; i++) {
832 TclPrintObject(stdout, objv[i], 15);
833 fprintf(stdout, " ");
834 }
835 fprintf(stdout, "\n");
836 fflush(stdout);
837 #else /* TCL_COMPILE_DEBUG */
838 fprintf(stdout, "%d: (%u) invoking %s\n",
839 iPtr->numLevels,
840 (unsigned int)(pc - codePtr->codeStart),
841 Tcl_GetString(objv[0]));
842 #endif /*TCL_COMPILE_DEBUG*/
843 }
844
845 iPtr->cmdCount++;
846 DECACHE_STACK_INFO();
847 result = (*cmdPtr->objProc)(cmdPtr->objClientData, interp,
848 objc, objv);
849 if (Tcl_AsyncReady()) {
850 result = Tcl_AsyncInvoke(interp, result);
851 }
852 CACHE_STACK_INFO();
853
854 /*
855 * If the interpreter has a non-empty string result, the
856 * result object is either empty or stale because some
857 * procedure set interp->result directly. If so, move the
858 * string result to the result object, then reset the
859 * string result.
860 */
861
862 if (*(iPtr->result) != 0) {
863 (void) Tcl_GetObjResult(interp);
864 }
865
866 /*
867 * Pop the objc top stack elements and decrement their ref
868 * counts.
869 */
870
871 for (i = 0; i < objc; i++) {
872 valuePtr = stackPtr[stackTop];
873 TclDecrRefCount(valuePtr);
874 stackTop--;
875 }
876
877 /*
878 * Process the result of the Tcl_ObjCmdProc call.
879 */
880
881 switch (result) {
882 case TCL_OK:
883 /*
884 * Push the call's object result and continue execution
885 * with the next instruction.
886 */
887 PUSH_OBJECT(Tcl_GetObjResult(interp));
888 TRACE_WITH_OBJ(("%u => ...after \"%.20s\", result=",
889 objc, cmdNameBuf), Tcl_GetObjResult(interp));
890 ADJUST_PC(pcAdjustment);
891
892 case TCL_BREAK:
893 case TCL_CONTINUE:
894 /*
895 * The invoked command requested a break or continue.
896 * Find the closest enclosing loop or catch exception
897 * range, if any. If a loop is found, terminate its
898 * execution or skip to its next iteration. If the
899 * closest is a catch exception range, jump to its
900 * catchOffset. If no enclosing range is found, stop
901 * execution and return the TCL_BREAK or TCL_CONTINUE.
902 */
903 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0,
904 codePtr);
905 if (rangePtr == NULL) {
906 TRACE(("%u => ... after \"%.20s\", no encl. loop or catch, returning %s\n",
907 objc, cmdNameBuf,
908 StringForResultCode(result)));
909 goto abnormalReturn; /* no catch exists to check */
910 }
911 newPcOffset = 0;
912 switch (rangePtr->type) {
913 case LOOP_EXCEPTION_RANGE:
914 if (result == TCL_BREAK) {
915 newPcOffset = rangePtr->breakOffset;
916 } else if (rangePtr->continueOffset == -1) {
917 TRACE(("%u => ... after \"%.20s\", %s, loop w/o continue, checking for catch\n",
918 objc, cmdNameBuf,
919 StringForResultCode(result)));
920 goto checkForCatch;
921 } else {
922 newPcOffset = rangePtr->continueOffset;
923 }
924 TRACE(("%u => ... after \"%.20s\", %s, range at %d, new pc %d\n",
925 objc, cmdNameBuf,
926 StringForResultCode(result),
927 rangePtr->codeOffset, newPcOffset));
928 break;
929 case CATCH_EXCEPTION_RANGE:
930 TRACE(("%u => ... after \"%.20s\", %s...\n",
931 objc, cmdNameBuf,
932 StringForResultCode(result)));
933 goto processCatch; /* it will use rangePtr */
934 default:
935 panic("TclExecuteByteCode: bad ExceptionRange type\n");
936 }
937 result = TCL_OK;
938 pc = (codePtr->codeStart + newPcOffset);
939 continue; /* restart outer instruction loop at pc */
940
941 case TCL_ERROR:
942 /*
943 * The invoked command returned an error. Look for an
944 * enclosing catch exception range, if any.
945 */
946 TRACE_WITH_OBJ(("%u => ... after \"%.20s\", TCL_ERROR ",
947 objc, cmdNameBuf), Tcl_GetObjResult(interp));
948 goto checkForCatch;
949
950 case TCL_RETURN:
951 /*
952 * The invoked command requested that the current
953 * procedure stop execution and return. First check
954 * for an enclosing catch exception range, if any.
955 */
956 TRACE(("%u => ... after \"%.20s\", TCL_RETURN\n",
957 objc, cmdNameBuf));
958 goto checkForCatch;
959
960 default:
961 TRACE_WITH_OBJ(("%u => ... after \"%.20s\", OTHER RETURN CODE %d ",
962 objc, cmdNameBuf, result),
963 Tcl_GetObjResult(interp));
964 goto checkForCatch;
965 }
966 }
967
968 case INST_EVAL_STK:
969 objPtr = POP_OBJECT();
970 DECACHE_STACK_INFO();
971 result = Tcl_EvalObjEx(interp, objPtr, 0);
972 CACHE_STACK_INFO();
973 if (result == TCL_OK) {
974 /*
975 * Normal return; push the eval's object result.
976 */
977 PUSH_OBJECT(Tcl_GetObjResult(interp));
978 TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)),
979 Tcl_GetObjResult(interp));
980 TclDecrRefCount(objPtr);
981 ADJUST_PC(1);
982 } else if ((result == TCL_BREAK) || (result == TCL_CONTINUE)) {
983 /*
984 * Find the closest enclosing loop or catch exception range,
985 * if any. If a loop is found, terminate its execution or
986 * skip to its next iteration. If the closest is a catch
987 * exception range, jump to its catchOffset. If no enclosing
988 * range is found, stop execution and return that same
989 * TCL_BREAK or TCL_CONTINUE.
990 */
991
992 int newPcOffset = 0; /* Pc offset computed during break,
993 * continue, error processing. Init.
994 * to avoid compiler warning. */
995
996 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0,
997 codePtr);
998 if (rangePtr == NULL) {
999 TRACE(("\"%.30s\" => no encl. loop or catch, returning %s\n",
1000 O2S(objPtr), StringForResultCode(result)));
1001 Tcl_DecrRefCount(objPtr);
1002 goto abnormalReturn; /* no catch exists to check */
1003 }
1004 switch (rangePtr->type) {
1005 case LOOP_EXCEPTION_RANGE:
1006 if (result == TCL_BREAK) {
1007 newPcOffset = rangePtr->breakOffset;
1008 } else if (rangePtr->continueOffset == -1) {
1009 TRACE(("\"%.30s\" => %s, loop w/o continue, checking for catch\n",
1010 O2S(objPtr), StringForResultCode(result)));
1011 Tcl_DecrRefCount(objPtr);
1012 goto checkForCatch;
1013 } else {
1014 newPcOffset = rangePtr->continueOffset;
1015 }
1016 result = TCL_OK;
1017 TRACE_WITH_OBJ(("\"%.30s\" => %s, range at %d, new pc %d ",
1018 O2S(objPtr), StringForResultCode(result),
1019 rangePtr->codeOffset, newPcOffset), valuePtr);
1020 break;
1021 case CATCH_EXCEPTION_RANGE:
1022 TRACE_WITH_OBJ(("\"%.30s\" => %s ",
1023 O2S(objPtr), StringForResultCode(result)),
1024 valuePtr);
1025 Tcl_DecrRefCount(objPtr);
1026 goto processCatch; /* it will use rangePtr */
1027 default:
1028 panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);
1029 }
1030 Tcl_DecrRefCount(objPtr);
1031 pc = (codePtr->codeStart + newPcOffset);
1032 continue; /* restart outer instruction loop at pc */
1033 } else { /* eval returned TCL_ERROR, TCL_RETURN, unknown code */
1034 TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)),
1035 Tcl_GetObjResult(interp));
1036 Tcl_DecrRefCount(objPtr);
1037 goto checkForCatch;
1038 }
1039
1040 case INST_EXPR_STK:
1041 objPtr = POP_OBJECT();
1042 Tcl_ResetResult(interp);
1043 DECACHE_STACK_INFO();
1044 result = Tcl_ExprObj(interp, objPtr, &valuePtr);
1045 CACHE_STACK_INFO();
1046 if (result != TCL_OK) {
1047 TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ",
1048 O2S(objPtr)), Tcl_GetObjResult(interp));
1049 Tcl_DecrRefCount(objPtr);
1050 goto checkForCatch;
1051 }
1052 stackPtr[++stackTop] = valuePtr; /* already has right refct */
1053 TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
1054 TclDecrRefCount(objPtr);
1055 ADJUST_PC(1);
1056
1057 case INST_LOAD_SCALAR1:
1058 #ifdef TCL_COMPILE_DEBUG
1059 opnd = TclGetUInt1AtPtr(pc+1);
1060 DECACHE_STACK_INFO();
1061 valuePtr = TclGetIndexedScalar(interp, opnd,
1062 /*leaveErrorMsg*/ 1);
1063 CACHE_STACK_INFO();
1064 if (valuePtr == NULL) {
1065 TRACE_WITH_OBJ(("%u => ERROR: ", opnd),
1066 Tcl_GetObjResult(interp));
1067 result = TCL_ERROR;
1068 goto checkForCatch;
1069 }
1070 PUSH_OBJECT(valuePtr);
1071 TRACE_WITH_OBJ(("%u => ", opnd), valuePtr);
1072 #else /* TCL_COMPILE_DEBUG */
1073 DECACHE_STACK_INFO();
1074 opnd = TclGetUInt1AtPtr(pc+1);
1075 valuePtr = TclGetIndexedScalar(interp, opnd, /*leaveErrorMsg*/ 1);
1076 CACHE_STACK_INFO();
1077 if (valuePtr == NULL) {
1078 result = TCL_ERROR;
1079 goto checkForCatch;
1080 }
1081 PUSH_OBJECT(valuePtr);
1082 #endif /* TCL_COMPILE_DEBUG */
1083 ADJUST_PC(2);
1084
1085 case INST_LOAD_SCALAR4:
1086 opnd = TclGetUInt4AtPtr(pc+1);
1087 DECACHE_STACK_INFO();
1088 valuePtr = TclGetIndexedScalar(interp, opnd,
1089 /*leaveErrorMsg*/ 1);
1090 CACHE_STACK_INFO();
1091 if (valuePtr == NULL) {
1092 TRACE_WITH_OBJ(("%u => ERROR: ", opnd),
1093 Tcl_GetObjResult(interp));
1094 result = TCL_ERROR;
1095 goto checkForCatch;
1096 }
1097 PUSH_OBJECT(valuePtr);
1098 TRACE_WITH_OBJ(("%u => ", opnd), valuePtr);
1099 ADJUST_PC(5);
1100
1101 case INST_LOAD_SCALAR_STK:
1102 objPtr = POP_OBJECT(); /* scalar name */
1103 DECACHE_STACK_INFO();
1104 valuePtr = Tcl_ObjGetVar2(interp, objPtr, NULL, TCL_LEAVE_ERR_MSG);
1105 CACHE_STACK_INFO();
1106 if (valuePtr == NULL) {
1107 TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ", O2S(objPtr)),
1108 Tcl_GetObjResult(interp));
1109 Tcl_DecrRefCount(objPtr);
1110 result = TCL_ERROR;
1111 goto checkForCatch;
1112 }
1113 PUSH_OBJECT(valuePtr);
1114 TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
1115 TclDecrRefCount(objPtr);
1116 ADJUST_PC(1);
1117
1118 case INST_LOAD_ARRAY4:
1119 opnd = TclGetUInt4AtPtr(pc+1);
1120 pcAdjustment = 5;
1121 goto doLoadArray;
1122
1123 case INST_LOAD_ARRAY1:
1124 opnd = TclGetUInt1AtPtr(pc+1);
1125 pcAdjustment = 2;
1126
1127 doLoadArray:
1128 {
1129 Tcl_Obj *elemPtr = POP_OBJECT();
1130
1131 DECACHE_STACK_INFO();
1132 valuePtr = TclGetElementOfIndexedArray(interp, opnd,
1133 elemPtr, /*leaveErrorMsg*/ 1);
1134 CACHE_STACK_INFO();
1135 if (valuePtr == NULL) {
1136 TRACE_WITH_OBJ(("%u \"%.30s\" => ERROR: ",
1137 opnd, O2S(elemPtr)), Tcl_GetObjResult(interp));
1138 Tcl_DecrRefCount(elemPtr);
1139 result = TCL_ERROR;
1140 goto checkForCatch;
1141 }
1142 PUSH_OBJECT(valuePtr);
1143 TRACE_WITH_OBJ(("%u \"%.30s\" => ",
1144 opnd, O2S(elemPtr)),valuePtr);
1145 TclDecrRefCount(elemPtr);
1146 }
1147 ADJUST_PC(pcAdjustment);
1148
1149 case INST_LOAD_ARRAY_STK:
1150 {
1151 Tcl_Obj *elemPtr = POP_OBJECT();
1152
1153 objPtr = POP_OBJECT(); /* array name */
1154 DECACHE_STACK_INFO();
1155 valuePtr = Tcl_ObjGetVar2(interp, objPtr, elemPtr,
1156 TCL_LEAVE_ERR_MSG);
1157 CACHE_STACK_INFO();
1158 if (valuePtr == NULL) {
1159 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" => ERROR: ",
1160 O2S(objPtr), O2S(elemPtr)),
1161 Tcl_GetObjResult(interp));
1162 Tcl_DecrRefCount(objPtr);
1163 Tcl_DecrRefCount(elemPtr);
1164 result = TCL_ERROR;
1165 goto checkForCatch;
1166 }
1167 PUSH_OBJECT(valuePtr);
1168 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" => ",
1169 O2S(objPtr), O2S(elemPtr)), valuePtr);
1170 TclDecrRefCount(objPtr);
1171 TclDecrRefCount(elemPtr);
1172 }
1173 ADJUST_PC(1);
1174
1175 case INST_LOAD_STK:
1176 objPtr = POP_OBJECT(); /* variable name */
1177 DECACHE_STACK_INFO();
1178 valuePtr = Tcl_ObjGetVar2(interp, objPtr, NULL, TCL_LEAVE_ERR_MSG);
1179 CACHE_STACK_INFO();
1180 if (valuePtr == NULL) {
1181 TRACE_WITH_OBJ(("\"%.30s\" => ERROR: ",
1182 O2S(objPtr)), Tcl_GetObjResult(interp));
1183 Tcl_DecrRefCount(objPtr);
1184 result = TCL_ERROR;
1185 goto checkForCatch;
1186 }
1187 PUSH_OBJECT(valuePtr);
1188 TRACE_WITH_OBJ(("\"%.30s\" => ", O2S(objPtr)), valuePtr);
1189 TclDecrRefCount(objPtr);
1190 ADJUST_PC(1);
1191
1192 case INST_STORE_SCALAR4:
1193 opnd = TclGetUInt4AtPtr(pc+1);
1194 pcAdjustment = 5;
1195 goto doStoreScalar;
1196
1197 case INST_STORE_SCALAR1:
1198 opnd = TclGetUInt1AtPtr(pc+1);
1199 pcAdjustment = 2;
1200
1201 doStoreScalar:
1202 valuePtr = POP_OBJECT();
1203 DECACHE_STACK_INFO();
1204 value2Ptr = TclSetIndexedScalar(interp, opnd, valuePtr,
1205 /*leaveErrorMsg*/ 1);
1206 CACHE_STACK_INFO();
1207 if (value2Ptr == NULL) {
1208 TRACE_WITH_OBJ(("%u <- \"%.30s\" => ERROR: ",
1209 opnd, O2S(valuePtr)), Tcl_GetObjResult(interp));
1210 Tcl_DecrRefCount(valuePtr);
1211 result = TCL_ERROR;
1212 goto checkForCatch;
1213 }
1214 PUSH_OBJECT(value2Ptr);
1215 TRACE_WITH_OBJ(("%u <- \"%.30s\" => ",
1216 opnd, O2S(valuePtr)), value2Ptr);
1217 TclDecrRefCount(valuePtr);
1218 ADJUST_PC(pcAdjustment);
1219
1220 case INST_STORE_SCALAR_STK:
1221 valuePtr = POP_OBJECT();
1222 objPtr = POP_OBJECT(); /* scalar name */
1223 DECACHE_STACK_INFO();
1224 value2Ptr = Tcl_ObjSetVar2(interp, objPtr, NULL, valuePtr,
1225 TCL_LEAVE_ERR_MSG);
1226 CACHE_STACK_INFO();
1227 if (value2Ptr == NULL) {
1228 TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ERROR: ",
1229 O2S(objPtr), O2S(valuePtr)),
1230 Tcl_GetObjResult(interp));
1231 Tcl_DecrRefCount(objPtr);
1232 Tcl_DecrRefCount(valuePtr);
1233 result = TCL_ERROR;
1234 goto checkForCatch;
1235 }
1236 PUSH_OBJECT(value2Ptr);
1237 TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ",
1238 O2S(objPtr), O2S(valuePtr)), value2Ptr);
1239 TclDecrRefCount(objPtr);
1240 TclDecrRefCount(valuePtr);
1241 ADJUST_PC(1);
1242
1243 case INST_STORE_ARRAY4:
1244 opnd = TclGetUInt4AtPtr(pc+1);
1245 pcAdjustment = 5;
1246 goto doStoreArray;
1247
1248 case INST_STORE_ARRAY1:
1249 opnd = TclGetUInt1AtPtr(pc+1);
1250 pcAdjustment = 2;
1251
1252 doStoreArray:
1253 {
1254 Tcl_Obj *elemPtr;
1255
1256 valuePtr = POP_OBJECT();
1257 elemPtr = POP_OBJECT();
1258 DECACHE_STACK_INFO();
1259 value2Ptr = TclSetElementOfIndexedArray(interp, opnd,
1260 elemPtr, valuePtr, TCL_LEAVE_ERR_MSG);
1261 CACHE_STACK_INFO();
1262 if (value2Ptr == NULL) {
1263 TRACE_WITH_OBJ(("%u \"%.30s\" <- \"%.30s\" => ERROR: ",
1264 opnd, O2S(elemPtr), O2S(valuePtr)),
1265 Tcl_GetObjResult(interp));
1266 Tcl_DecrRefCount(elemPtr);
1267 Tcl_DecrRefCount(valuePtr);
1268 result = TCL_ERROR;
1269 goto checkForCatch;
1270 }
1271 PUSH_OBJECT(value2Ptr);
1272 TRACE_WITH_OBJ(("%u \"%.30s\" <- \"%.30s\" => ",
1273 opnd, O2S(elemPtr), O2S(valuePtr)), value2Ptr);
1274 TclDecrRefCount(elemPtr);
1275 TclDecrRefCount(valuePtr);
1276 }
1277 ADJUST_PC(pcAdjustment);
1278
1279 case INST_STORE_ARRAY_STK:
1280 {
1281 Tcl_Obj *elemPtr;
1282
1283 valuePtr = POP_OBJECT();
1284 elemPtr = POP_OBJECT();
1285 objPtr = POP_OBJECT(); /* array name */
1286 DECACHE_STACK_INFO();
1287 value2Ptr = Tcl_ObjSetVar2(interp, objPtr, elemPtr, valuePtr,
1288 TCL_LEAVE_ERR_MSG);
1289 CACHE_STACK_INFO();
1290 if (value2Ptr == NULL) {
1291 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ERROR: ",
1292 O2S(objPtr), O2S(elemPtr), O2S(valuePtr)),
1293 Tcl_GetObjResult(interp));
1294 Tcl_DecrRefCount(objPtr);
1295 Tcl_DecrRefCount(elemPtr);
1296 Tcl_DecrRefCount(valuePtr);
1297 result = TCL_ERROR;
1298 goto checkForCatch;
1299 }
1300 PUSH_OBJECT(value2Ptr);
1301 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" <- \"%.30s\" => ",
1302 O2S(objPtr), O2S(elemPtr), O2S(valuePtr)),
1303 value2Ptr);
1304 TclDecrRefCount(objPtr);
1305 TclDecrRefCount(elemPtr);
1306 TclDecrRefCount(valuePtr);
1307 }
1308 ADJUST_PC(1);
1309
1310 case INST_STORE_STK:
1311 valuePtr = POP_OBJECT();
1312 objPtr = POP_OBJECT(); /* variable name */
1313 DECACHE_STACK_INFO();
1314 value2Ptr = Tcl_ObjSetVar2(interp, objPtr, NULL, valuePtr,
1315 TCL_LEAVE_ERR_MSG);
1316 CACHE_STACK_INFO();
1317 if (value2Ptr == NULL) {
1318 TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ERROR: ",
1319 O2S(objPtr), O2S(valuePtr)),
1320 Tcl_GetObjResult(interp));
1321 Tcl_DecrRefCount(objPtr);
1322 Tcl_DecrRefCount(valuePtr);
1323 result = TCL_ERROR;
1324 goto checkForCatch;
1325 }
1326 PUSH_OBJECT(value2Ptr);
1327 TRACE_WITH_OBJ(("\"%.30s\" <- \"%.30s\" => ",
1328 O2S(objPtr), O2S(valuePtr)), value2Ptr);
1329 TclDecrRefCount(objPtr);
1330 TclDecrRefCount(valuePtr);
1331 ADJUST_PC(1);
1332
1333 case INST_INCR_SCALAR1:
1334 opnd = TclGetUInt1AtPtr(pc+1);
1335 valuePtr = POP_OBJECT();
1336 if (valuePtr->typePtr != &tclIntType) {
1337 result = tclIntType.setFromAnyProc(interp, valuePtr);
1338 if (result != TCL_OK) {
1339 TRACE_WITH_OBJ(("%u (by %s) => ERROR converting increment amount to int: ",
1340 opnd, O2S(valuePtr)), Tcl_GetObjResult(interp));
1341 Tcl_DecrRefCount(valuePtr);
1342 goto checkForCatch;
1343 }
1344 }
1345 i = valuePtr->internalRep.longValue;
1346 DECACHE_STACK_INFO();
1347 value2Ptr = TclIncrIndexedScalar(interp, opnd, i);
1348 CACHE_STACK_INFO();
1349 if (value2Ptr == NULL) {
1350 TRACE_WITH_OBJ(("%u (by %ld) => ERROR: ", opnd, i),
1351 Tcl_GetObjResult(interp));
1352 Tcl_DecrRefCount(valuePtr);
1353 result = TCL_ERROR;
1354 goto checkForCatch;
1355 }
1356 PUSH_OBJECT(value2Ptr);
1357 TRACE_WITH_OBJ(("%u (by %ld) => ", opnd, i), value2Ptr);
1358 TclDecrRefCount(valuePtr);
1359 ADJUST_PC(2);
1360
1361 case INST_INCR_SCALAR_STK:
1362 case INST_INCR_STK:
1363 valuePtr = POP_OBJECT();
1364 objPtr = POP_OBJECT(); /* scalar name */
1365 if (valuePtr->typePtr != &tclIntType) {
1366 result = tclIntType.setFromAnyProc(interp, valuePtr);
1367 if (result != TCL_OK) {
1368 TRACE_WITH_OBJ(("\"%.30s\" (by %s) => ERROR converting increment amount to int: ",
1369 O2S(objPtr), O2S(valuePtr)),
1370 Tcl_GetObjResult(interp));
1371 Tcl_DecrRefCount(objPtr);
1372 Tcl_DecrRefCount(valuePtr);
1373 goto checkForCatch;
1374 }
1375 }
1376 i = valuePtr->internalRep.longValue;
1377 DECACHE_STACK_INFO();
1378 value2Ptr = TclIncrVar2(interp, objPtr, (Tcl_Obj *) NULL, i,
1379 TCL_LEAVE_ERR_MSG);
1380 CACHE_STACK_INFO();
1381 if (value2Ptr == NULL) {
1382 TRACE_WITH_OBJ(("\"%.30s\" (by %ld) => ERROR: ",
1383 O2S(objPtr), i), Tcl_GetObjResult(interp));
1384 Tcl_DecrRefCount(objPtr);
1385 Tcl_DecrRefCount(valuePtr);
1386 result = TCL_ERROR;
1387 goto checkForCatch;
1388 }
1389 PUSH_OBJECT(value2Ptr);
1390 TRACE_WITH_OBJ(("\"%.30s\" (by %ld) => ", O2S(objPtr), i),
1391 value2Ptr);
1392 Tcl_DecrRefCount(objPtr);
1393 Tcl_DecrRefCount(valuePtr);
1394 ADJUST_PC(1);
1395
1396 case INST_INCR_ARRAY1:
1397 {
1398 Tcl_Obj *elemPtr;
1399
1400 opnd = TclGetUInt1AtPtr(pc+1);
1401 valuePtr = POP_OBJECT();
1402 elemPtr = POP_OBJECT();
1403 if (valuePtr->typePtr != &tclIntType) {
1404 result = tclIntType.setFromAnyProc(interp, valuePtr);
1405 if (result != TCL_OK) {
1406 TRACE_WITH_OBJ(("%u \"%.30s\" (by %s) => ERROR converting increment amount to int: ",
1407 opnd, O2S(elemPtr), O2S(valuePtr)),
1408 Tcl_GetObjResult(interp));
1409 Tcl_DecrRefCount(elemPtr);
1410 Tcl_DecrRefCount(valuePtr);
1411 goto checkForCatch;
1412 }
1413 }
1414 i = valuePtr->internalRep.longValue;
1415 DECACHE_STACK_INFO();
1416 value2Ptr = TclIncrElementOfIndexedArray(interp, opnd,
1417 elemPtr, i);
1418 CACHE_STACK_INFO();
1419 if (value2Ptr == NULL) {
1420 TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ERROR: ",
1421 opnd, O2S(elemPtr), i),
1422 Tcl_GetObjResult(interp));
1423 Tcl_DecrRefCount(elemPtr);
1424 Tcl_DecrRefCount(valuePtr);
1425 result = TCL_ERROR;
1426 goto checkForCatch;
1427 }
1428 PUSH_OBJECT(value2Ptr);
1429 TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ",
1430 opnd, O2S(elemPtr), i), value2Ptr);
1431 Tcl_DecrRefCount(elemPtr);
1432 Tcl_DecrRefCount(valuePtr);
1433 }
1434 ADJUST_PC(2);
1435
1436 case INST_INCR_ARRAY_STK:
1437 {
1438 Tcl_Obj *elemPtr;
1439
1440 valuePtr = POP_OBJECT();
1441 elemPtr = POP_OBJECT();
1442 objPtr = POP_OBJECT(); /* array name */
1443 if (valuePtr->typePtr != &tclIntType) {
1444 result = tclIntType.setFromAnyProc(interp, valuePtr);
1445 if (result != TCL_OK) {
1446 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %s) => ERROR converting increment amount to int: ",
1447 O2S(objPtr), O2S(elemPtr), O2S(valuePtr)),
1448 Tcl_GetObjResult(interp));
1449 Tcl_DecrRefCount(objPtr);
1450 Tcl_DecrRefCount(elemPtr);
1451 Tcl_DecrRefCount(valuePtr);
1452 goto checkForCatch;
1453 }
1454 }
1455 i = valuePtr->internalRep.longValue;
1456 DECACHE_STACK_INFO();
1457 value2Ptr = TclIncrVar2(interp, objPtr, elemPtr, i,
1458 TCL_LEAVE_ERR_MSG);
1459 CACHE_STACK_INFO();
1460 if (value2Ptr == NULL) {
1461 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ERROR: ",
1462 O2S(objPtr), O2S(elemPtr), i),
1463 Tcl_GetObjResult(interp));
1464 Tcl_DecrRefCount(objPtr);
1465 Tcl_DecrRefCount(elemPtr);
1466 Tcl_DecrRefCount(valuePtr);
1467 result = TCL_ERROR;
1468 goto checkForCatch;
1469 }
1470 PUSH_OBJECT(value2Ptr);
1471 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ",
1472 O2S(objPtr), O2S(elemPtr), i), value2Ptr);
1473 Tcl_DecrRefCount(objPtr);
1474 Tcl_DecrRefCount(elemPtr);
1475 Tcl_DecrRefCount(valuePtr);
1476 }
1477 ADJUST_PC(1);
1478
1479 case INST_INCR_SCALAR1_IMM:
1480 opnd = TclGetUInt1AtPtr(pc+1);
1481 i = TclGetInt1AtPtr(pc+2);
1482 DECACHE_STACK_INFO();
1483 value2Ptr = TclIncrIndexedScalar(interp, opnd, i);
1484 CACHE_STACK_INFO();
1485 if (value2Ptr == NULL) {
1486 TRACE_WITH_OBJ(("%u %ld => ERROR: ", opnd, i),
1487 Tcl_GetObjResult(interp));
1488 result = TCL_ERROR;
1489 goto checkForCatch;
1490 }
1491 PUSH_OBJECT(value2Ptr);
1492 TRACE_WITH_OBJ(("%u %ld => ", opnd, i), value2Ptr);
1493 ADJUST_PC(3);
1494
1495 case INST_INCR_SCALAR_STK_IMM:
1496 case INST_INCR_STK_IMM:
1497 objPtr = POP_OBJECT(); /* variable name */
1498 i = TclGetInt1AtPtr(pc+1);
1499 DECACHE_STACK_INFO();
1500 value2Ptr = TclIncrVar2(interp, objPtr, (Tcl_Obj *) NULL, i,
1501 TCL_LEAVE_ERR_MSG);
1502 CACHE_STACK_INFO();
1503 if (value2Ptr == NULL) {
1504 TRACE_WITH_OBJ(("\"%.30s\" %ld => ERROR: ",
1505 O2S(objPtr), i), Tcl_GetObjResult(interp));
1506 result = TCL_ERROR;
1507 Tcl_DecrRefCount(objPtr);
1508 goto checkForCatch;
1509 }
1510 PUSH_OBJECT(value2Ptr);
1511 TRACE_WITH_OBJ(("\"%.30s\" %ld => ", O2S(objPtr), i),
1512 value2Ptr);
1513 TclDecrRefCount(objPtr);
1514 ADJUST_PC(2);
1515
1516 case INST_INCR_ARRAY1_IMM:
1517 {
1518 Tcl_Obj *elemPtr;
1519
1520 opnd = TclGetUInt1AtPtr(pc+1);
1521 i = TclGetInt1AtPtr(pc+2);
1522 elemPtr = POP_OBJECT();
1523 DECACHE_STACK_INFO();
1524 value2Ptr = TclIncrElementOfIndexedArray(interp, opnd,
1525 elemPtr, i);
1526 CACHE_STACK_INFO();
1527 if (value2Ptr == NULL) {
1528 TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ERROR: ",
1529 opnd, O2S(elemPtr), i),
1530 Tcl_GetObjResult(interp));
1531 Tcl_DecrRefCount(elemPtr);
1532 result = TCL_ERROR;
1533 goto checkForCatch;
1534 }
1535 PUSH_OBJECT(value2Ptr);
1536 TRACE_WITH_OBJ(("%u \"%.30s\" (by %ld) => ",
1537 opnd, O2S(elemPtr), i), value2Ptr);
1538 Tcl_DecrRefCount(elemPtr);
1539 }
1540 ADJUST_PC(3);
1541
1542 case INST_INCR_ARRAY_STK_IMM:
1543 {
1544 Tcl_Obj *elemPtr;
1545
1546 i = TclGetInt1AtPtr(pc+1);
1547 elemPtr = POP_OBJECT();
1548 objPtr = POP_OBJECT(); /* array name */
1549 DECACHE_STACK_INFO();
1550 value2Ptr = TclIncrVar2(interp, objPtr, elemPtr, i,
1551 TCL_LEAVE_ERR_MSG);
1552 CACHE_STACK_INFO();
1553 if (value2Ptr == NULL) {
1554 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ERROR: ",
1555 O2S(objPtr), O2S(elemPtr), i),
1556 Tcl_GetObjResult(interp));
1557 Tcl_DecrRefCount(objPtr);
1558 Tcl_DecrRefCount(elemPtr);
1559 result = TCL_ERROR;
1560 goto checkForCatch;
1561 }
1562 PUSH_OBJECT(value2Ptr);
1563 TRACE_WITH_OBJ(("\"%.30s(%.30s)\" (by %ld) => ",
1564 O2S(objPtr), O2S(elemPtr), i), value2Ptr);
1565 Tcl_DecrRefCount(objPtr);
1566 Tcl_DecrRefCount(elemPtr);
1567 }
1568 ADJUST_PC(2);
1569
1570 case INST_JUMP1:
1571 #ifdef TCL_COMPILE_DEBUG
1572 opnd = TclGetInt1AtPtr(pc+1);
1573 TRACE(("%d => new pc %u\n", opnd,
1574 (unsigned int)(pc + opnd - codePtr->codeStart)));
1575 pc += opnd;
1576 #else
1577 pc += TclGetInt1AtPtr(pc+1);
1578 #endif /* TCL_COMPILE_DEBUG */
1579 continue;
1580
1581 case INST_JUMP4:
1582 opnd = TclGetInt4AtPtr(pc+1);
1583 TRACE(("%d => new pc %u\n", opnd,
1584 (unsigned int)(pc + opnd - codePtr->codeStart)));
1585 ADJUST_PC(opnd);
1586
1587 case INST_JUMP_TRUE4:
1588 opnd = TclGetInt4AtPtr(pc+1);
1589 pcAdjustment = 5;
1590 goto doJumpTrue;
1591
1592 case INST_JUMP_TRUE1:
1593 opnd = TclGetInt1AtPtr(pc+1);
1594 pcAdjustment = 2;
1595
1596 doJumpTrue:
1597 {
1598 int b;
1599
1600 valuePtr = POP_OBJECT();
1601 if (valuePtr->typePtr == &tclIntType) {
1602 b = (valuePtr->internalRep.longValue != 0);
1603 } else if (valuePtr->typePtr == &tclDoubleType) {
1604 b = (valuePtr->internalRep.doubleValue != 0.0);
1605 } else {
1606 result = Tcl_GetBooleanFromObj(interp, valuePtr, &b);
1607 if (result != TCL_OK) {
1608 TRACE_WITH_OBJ(("%d => ERROR: ", opnd),
1609 Tcl_GetObjResult(interp));
1610 Tcl_DecrRefCount(valuePtr);
1611 goto checkForCatch;
1612 }
1613 }
1614 if (b) {
1615 TRACE(("%d => %.20s true, new pc %u\n",
1616 opnd, O2S(valuePtr),
1617 (unsigned int)(pc+opnd - codePtr->codeStart)));
1618 TclDecrRefCount(valuePtr);
1619 ADJUST_PC(opnd);
1620 } else {
1621 TRACE(("%d => %.20s false\n", opnd, O2S(valuePtr)));
1622 TclDecrRefCount(valuePtr);
1623 ADJUST_PC(pcAdjustment);
1624 }
1625 }
1626
1627 case INST_JUMP_FALSE4:
1628 opnd = TclGetInt4AtPtr(pc+1);
1629 pcAdjustment = 5;
1630 goto doJumpFalse;
1631
1632 case INST_JUMP_FALSE1:
1633 opnd = TclGetInt1AtPtr(pc+1);
1634 pcAdjustment = 2;
1635
1636 doJumpFalse:
1637 {
1638 int b;
1639
1640 valuePtr = POP_OBJECT();
1641 if (valuePtr->typePtr == &tclIntType) {
1642 b = (valuePtr->internalRep.longValue != 0);
1643 } else if (valuePtr->typePtr == &tclDoubleType) {
1644 b = (valuePtr->internalRep.doubleValue != 0.0);
1645 } else {
1646 result = Tcl_GetBooleanFromObj(interp, valuePtr, &b);
1647 if (result != TCL_OK) {
1648 TRACE_WITH_OBJ(("%d => ERROR: ", opnd),
1649 Tcl_GetObjResult(interp));
1650 Tcl_DecrRefCount(valuePtr);
1651 goto checkForCatch;
1652 }
1653 }
1654 if (b) {
1655 TRACE(("%d => %.20s true\n", opnd, O2S(valuePtr)));
1656 TclDecrRefCount(valuePtr);
1657 ADJUST_PC(pcAdjustment);
1658 } else {
1659 TRACE(("%d => %.20s false, new pc %u\n",
1660 opnd, O2S(valuePtr),
1661 (unsigned int)(pc + opnd - codePtr->codeStart)));
1662 TclDecrRefCount(valuePtr);
1663 ADJUST_PC(opnd);
1664 }
1665 }
1666
1667 case INST_LOR:
1668 case INST_LAND:
1669 {
1670 /*
1671 * Operands must be boolean or numeric. No int->double
1672 * conversions are performed.
1673 */
1674
1675 int i1, i2;
1676 int iResult;
1677 char *s;
1678 Tcl_ObjType *t1Ptr, *t2Ptr;
1679
1680 value2Ptr = POP_OBJECT();
1681 valuePtr = POP_OBJECT();
1682 t1Ptr = valuePtr->typePtr;
1683 t2Ptr = value2Ptr->typePtr;
1684
1685 if ((t1Ptr == &tclIntType) || (t1Ptr == &tclBooleanType)) {
1686 i1 = (valuePtr->internalRep.longValue != 0);
1687 } else if (t1Ptr == &tclDoubleType) {
1688 i1 = (valuePtr->internalRep.doubleValue != 0.0);
1689 } else {
1690 s = Tcl_GetStringFromObj(valuePtr, &length);
1691 if (TclLooksLikeInt(s, length)) {
1692 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
1693 valuePtr, &i);
1694 i1 = (i != 0);
1695 } else {
1696 result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL,
1697 valuePtr, &i1);
1698 i1 = (i1 != 0);
1699 }
1700 if (result != TCL_OK) {
1701 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
1702 O2S(valuePtr),
1703 (t1Ptr? t1Ptr->name : "null")));
1704 IllegalExprOperandType(interp, pc, valuePtr);
1705 Tcl_DecrRefCount(valuePtr);
1706 Tcl_DecrRefCount(value2Ptr);
1707 goto checkForCatch;
1708 }
1709 }
1710
1711 if ((t2Ptr == &tclIntType) || (t2Ptr == &tclBooleanType)) {
1712 i2 = (value2Ptr->internalRep.longValue != 0);
1713 } else if (t2Ptr == &tclDoubleType) {
1714 i2 = (value2Ptr->internalRep.doubleValue != 0.0);
1715 } else {
1716 s = Tcl_GetStringFromObj(value2Ptr, &length);
1717 if (TclLooksLikeInt(s, length)) {
1718 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
1719 value2Ptr, &i);
1720 i2 = (i != 0);
1721 } else {
1722 result = Tcl_GetBooleanFromObj((Tcl_Interp *) NULL,
1723 value2Ptr, &i2);
1724 }
1725 if (result != TCL_OK) {
1726 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
1727 O2S(value2Ptr),
1728 (t2Ptr? t2Ptr->name : "null")));
1729 IllegalExprOperandType(interp, pc, value2Ptr);
1730 Tcl_DecrRefCount(valuePtr);
1731 Tcl_DecrRefCount(value2Ptr);
1732 goto checkForCatch;
1733 }
1734 }
1735
1736 /*
1737 * Reuse the valuePtr object already on stack if possible.
1738 */
1739
1740 if (*pc == INST_LOR) {
1741 iResult = (i1 || i2);
1742 } else {
1743 iResult = (i1 && i2);
1744 }
1745 if (Tcl_IsShared(valuePtr)) {
1746 PUSH_OBJECT(Tcl_NewLongObj(iResult));
1747 TRACE(("%.20s %.20s => %d\n",
1748 O2S(valuePtr), O2S(value2Ptr), iResult));
1749 TclDecrRefCount(valuePtr);
1750 } else { /* reuse the valuePtr object */
1751 TRACE(("%.20s %.20s => %d\n",
1752 O2S(valuePtr), O2S(value2Ptr), iResult));
1753 Tcl_SetLongObj(valuePtr, iResult);
1754 ++stackTop; /* valuePtr now on stk top has right r.c. */
1755 }
1756 TclDecrRefCount(value2Ptr);
1757 }
1758 ADJUST_PC(1);
1759
1760 case INST_EQ:
1761 case INST_NEQ:
1762 case INST_LT:
1763 case INST_GT:
1764 case INST_LE:
1765 case INST_GE:
1766 {
1767 /*
1768 * Any type is allowed but the two operands must have the
1769 * same type. We will compute value op value2.
1770 */
1771
1772 Tcl_ObjType *t1Ptr, *t2Ptr;
1773 char *s1 = NULL; /* Init. avoids compiler warning. */
1774 char *s2 = NULL; /* Init. avoids compiler warning. */
1775 long i2 = 0; /* Init. avoids compiler warning. */
1776 double d1 = 0.0; /* Init. avoids compiler warning. */
1777 double d2 = 0.0; /* Init. avoids compiler warning. */
1778 long iResult = 0; /* Init. avoids compiler warning. */
1779
1780 value2Ptr = POP_OBJECT();
1781 valuePtr = POP_OBJECT();
1782 t1Ptr = valuePtr->typePtr;
1783 t2Ptr = value2Ptr->typePtr;
1784
1785 /*
1786 * We only want to coerce numeric validation if
1787 * neither type is NULL. A NULL type means the arg is
1788 * essentially an empty object ("", {} or [list]).
1789 */
1790 if (!((((t1Ptr == NULL) && (valuePtr->bytes == NULL))
1791 || (valuePtr->bytes && (valuePtr->length == 0)))
1792 || (((t2Ptr == NULL) && (value2Ptr->bytes == NULL))
1793 || (value2Ptr->bytes && (value2Ptr->length == 0))))) {
1794 if ((t1Ptr != &tclIntType) && (t1Ptr != &tclDoubleType)) {
1795 s1 = Tcl_GetStringFromObj(valuePtr, &length);
1796 if (TclLooksLikeInt(s1, length)) {
1797 (void) Tcl_GetLongFromObj((Tcl_Interp *) NULL,
1798 valuePtr, &i);
1799 } else {
1800 (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
1801 valuePtr, &d1);
1802 }
1803 t1Ptr = valuePtr->typePtr;
1804 }
1805 if ((t2Ptr != &tclIntType) && (t2Ptr != &tclDoubleType)) {
1806 s2 = Tcl_GetStringFromObj(value2Ptr, &length);
1807 if (TclLooksLikeInt(s2, length)) {
1808 (void) Tcl_GetLongFromObj((Tcl_Interp *) NULL,
1809 value2Ptr, &i2);
1810 } else {
1811 (void) Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
1812 value2Ptr, &d2);
1813 }
1814 t2Ptr = value2Ptr->typePtr;
1815 }
1816 }
1817 if (((t1Ptr != &tclIntType) && (t1Ptr != &tclDoubleType))
1818 || ((t2Ptr != &tclIntType) && (t2Ptr != &tclDoubleType))) {
1819 /*
1820 * One operand is not numeric. Compare as strings.
1821 */
1822 int cmpValue;
1823 s1 = Tcl_GetString(valuePtr);
1824 s2 = Tcl_GetString(value2Ptr);
1825 cmpValue = strcmp(s1, s2);
1826 switch (*pc) {
1827 case INST_EQ:
1828 iResult = (cmpValue == 0);
1829 break;
1830 case INST_NEQ:
1831 iResult = (cmpValue != 0);
1832 break;
1833 case INST_LT:
1834 iResult = (cmpValue < 0);
1835 break;
1836 case INST_GT:
1837 iResult = (cmpValue > 0);
1838 break;
1839 case INST_LE:
1840 iResult = (cmpValue <= 0);
1841 break;
1842 case INST_GE:
1843 iResult = (cmpValue >= 0);
1844 break;
1845 }
1846 } else if ((t1Ptr == &tclDoubleType)
1847 || (t2Ptr == &tclDoubleType)) {
1848 /*
1849 * Compare as doubles.
1850 */
1851 if (t1Ptr == &tclDoubleType) {
1852 d1 = valuePtr->internalRep.doubleValue;
1853 if (t2Ptr == &tclIntType) {
1854 d2 = value2Ptr->internalRep.longValue;
1855 } else {
1856 d2 = value2Ptr->internalRep.doubleValue;
1857 }
1858 } else { /* t1Ptr is int, t2Ptr is double */
1859 d1 = valuePtr->internalRep.longValue;
1860 d2 = value2Ptr->internalRep.doubleValue;
1861 }
1862 switch (*pc) {
1863 case INST_EQ:
1864 iResult = d1 == d2;
1865 break;
1866 case INST_NEQ:
1867 iResult = d1 != d2;
1868 break;
1869 case INST_LT:
1870 iResult = d1 < d2;
1871 break;
1872 case INST_GT:
1873 iResult = d1 > d2;
1874 break;
1875 case INST_LE:
1876 iResult = d1 <= d2;
1877 break;
1878 case INST_GE:
1879 iResult = d1 >= d2;
1880 break;
1881 }
1882 } else {
1883 /*
1884 * Compare as ints.
1885 */
1886 i = valuePtr->internalRep.longValue;
1887 i2 = value2Ptr->internalRep.longValue;
1888 switch (*pc) {
1889 case INST_EQ:
1890 iResult = i == i2;
1891 break;
1892 case INST_NEQ:
1893 iResult = i != i2;
1894 break;
1895 case INST_LT:
1896 iResult = i < i2;
1897 break;
1898 case INST_GT:
1899 iResult = i > i2;
1900 break;
1901 case INST_LE:
1902 iResult = i <= i2;
1903 break;
1904 case INST_GE:
1905 iResult = i >= i2;
1906 break;
1907 }
1908 }
1909
1910 /*
1911 * Reuse the valuePtr object already on stack if possible.
1912 */
1913
1914 if (Tcl_IsShared(valuePtr)) {
1915 PUSH_OBJECT(Tcl_NewLongObj(iResult));
1916 TRACE(("%.20s %.20s => %ld\n",
1917 O2S(valuePtr), O2S(value2Ptr), iResult));
1918 TclDecrRefCount(valuePtr);
1919 } else { /* reuse the valuePtr object */
1920 TRACE(("%.20s %.20s => %ld\n",
1921 O2S(valuePtr), O2S(value2Ptr), iResult));
1922 Tcl_SetLongObj(valuePtr, iResult);
1923 ++stackTop; /* valuePtr now on stk top has right r.c. */
1924 }
1925 TclDecrRefCount(value2Ptr);
1926 }
1927 ADJUST_PC(1);
1928
1929 case INST_MOD:
1930 case INST_LSHIFT:
1931 case INST_RSHIFT:
1932 case INST_BITOR:
1933 case INST_BITXOR:
1934 case INST_BITAND:
1935 {
1936 /*
1937 * Only integers are allowed. We compute value op value2.
1938 */
1939
1940 long i2, rem, negative;
1941 long iResult = 0; /* Init. avoids compiler warning. */
1942
1943 value2Ptr = POP_OBJECT();
1944 valuePtr = POP_OBJECT();
1945 if (valuePtr->typePtr == &tclIntType) {
1946 i = valuePtr->internalRep.longValue;
1947 } else { /* try to convert to int */
1948 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
1949 valuePtr, &i);
1950 if (result != TCL_OK) {
1951 TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n",
1952 O2S(valuePtr), O2S(value2Ptr),
1953 (valuePtr->typePtr?
1954 valuePtr->typePtr->name : "null")));
1955 IllegalExprOperandType(interp, pc, valuePtr);
1956 Tcl_DecrRefCount(valuePtr);
1957 Tcl_DecrRefCount(value2Ptr);
1958 goto checkForCatch;
1959 }
1960 }
1961 if (value2Ptr->typePtr == &tclIntType) {
1962 i2 = value2Ptr->internalRep.longValue;
1963 } else {
1964 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
1965 value2Ptr, &i2);
1966 if (result != TCL_OK) {
1967 TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n",
1968 O2S(valuePtr), O2S(value2Ptr),
1969 (value2Ptr->typePtr?
1970 value2Ptr->typePtr->name : "null")));
1971 IllegalExprOperandType(interp, pc, value2Ptr);
1972 Tcl_DecrRefCount(valuePtr);
1973 Tcl_DecrRefCount(value2Ptr);
1974 goto checkForCatch;
1975 }
1976 }
1977
1978 switch (*pc) {
1979 case INST_MOD:
1980 /*
1981 * This code is tricky: C doesn't guarantee much about
1982 * the quotient or remainder, but Tcl does. The
1983 * remainder always has the same sign as the divisor and
1984 * a smaller absolute value.
1985 */
1986 if (i2 == 0) {
1987 TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
1988 Tcl_DecrRefCount(valuePtr);
1989 Tcl_DecrRefCount(value2Ptr);
1990 goto divideByZero;
1991 }
1992 negative = 0;
1993 if (i2 < 0) {
1994 i2 = -i2;
1995 i = -i;
1996 negative = 1;
1997 }
1998 rem = i % i2;
1999 if (rem < 0) {
2000 rem += i2;
2001 }
2002 if (negative) {
2003 rem = -rem;
2004 }
2005 iResult = rem;
2006 break;
2007 case INST_LSHIFT:
2008 iResult = i << i2;
2009 break;
2010 case INST_RSHIFT:
2011 /*
2012 * The following code is a bit tricky: it ensures that
2013 * right shifts propagate the sign bit even on machines
2014 * where ">>" won't do it by default.
2015 */
2016 if (i < 0) {
2017 iResult = ~((~i) >> i2);
2018 } else {
2019 iResult = i >> i2;
2020 }
2021 break;
2022 case INST_BITOR:
2023 iResult = i | i2;
2024 break;
2025 case INST_BITXOR:
2026 iResult = i ^ i2;
2027 break;
2028 case INST_BITAND:
2029 iResult = i & i2;
2030 break;
2031 }
2032
2033 /*
2034 * Reuse the valuePtr object already on stack if possible.
2035 */
2036
2037 if (Tcl_IsShared(valuePtr)) {
2038 PUSH_OBJECT(Tcl_NewLongObj(iResult));
2039 TRACE(("%ld %ld => %ld\n", i, i2, iResult));
2040 TclDecrRefCount(valuePtr);
2041 } else { /* reuse the valuePtr object */
2042 TRACE(("%ld %ld => %ld\n", i, i2, iResult));
2043 Tcl_SetLongObj(valuePtr, iResult);
2044 ++stackTop; /* valuePtr now on stk top has right r.c. */
2045 }
2046 TclDecrRefCount(value2Ptr);
2047 }
2048 ADJUST_PC(1);
2049
2050 case INST_ADD:
2051 case INST_SUB:
2052 case INST_MULT:
2053 case INST_DIV:
2054 {
2055 /*
2056 * Operands must be numeric and ints get converted to floats
2057 * if necessary. We compute value op value2.
2058 */
2059
2060 Tcl_ObjType *t1Ptr, *t2Ptr;
2061 long i2, quot, rem;
2062 double d1, d2;
2063 long iResult = 0; /* Init. avoids compiler warning. */
2064 double dResult = 0.0; /* Init. avoids compiler warning. */
2065 int doDouble = 0; /* 1 if doing floating arithmetic */
2066
2067 value2Ptr = POP_OBJECT();
2068 valuePtr = POP_OBJECT();
2069 t1Ptr = valuePtr->typePtr;
2070 t2Ptr = value2Ptr->typePtr;
2071
2072 if (t1Ptr == &tclIntType) {
2073 i = valuePtr->internalRep.longValue;
2074 } else if ((t1Ptr == &tclDoubleType)
2075 && (valuePtr->bytes == NULL)) {
2076 /*
2077 * We can only use the internal rep directly if there is
2078 * no string rep. Otherwise the string rep might actually
2079 * look like an integer, which is preferred.
2080 */
2081
2082 d1 = valuePtr->internalRep.doubleValue;
2083 } else {
2084 char *s = Tcl_GetStringFromObj(valuePtr, &length);
2085 if (TclLooksLikeInt(s, length)) {
2086 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
2087 valuePtr, &i);
2088 } else {
2089 result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
2090 valuePtr, &d1);
2091 }
2092 if (result != TCL_OK) {
2093 TRACE(("%.20s %.20s => ILLEGAL 1st TYPE %s\n",
2094 s, O2S(valuePtr),
2095 (valuePtr->typePtr?
2096 valuePtr->typePtr->name : "null")));
2097 IllegalExprOperandType(interp, pc, valuePtr);
2098 Tcl_DecrRefCount(valuePtr);
2099 Tcl_DecrRefCount(value2Ptr);
2100 goto checkForCatch;
2101 }
2102 t1Ptr = valuePtr->typePtr;
2103 }
2104
2105 if (t2Ptr == &tclIntType) {
2106 i2 = value2Ptr->internalRep.longValue;
2107 } else if ((t2Ptr == &tclDoubleType)
2108 && (value2Ptr->bytes == NULL)) {
2109 /*
2110 * We can only use the internal rep directly if there is
2111 * no string rep. Otherwise the string rep might actually
2112 * look like an integer, which is preferred.
2113 */
2114
2115 d2 = value2Ptr->internalRep.doubleValue;
2116 } else {
2117 char *s = Tcl_GetStringFromObj(value2Ptr, &length);
2118 if (TclLooksLikeInt(s, length)) {
2119 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
2120 value2Ptr, &i2);
2121 } else {
2122 result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
2123 value2Ptr, &d2);
2124 }
2125 if (result != TCL_OK) {
2126 TRACE(("%.20s %.20s => ILLEGAL 2nd TYPE %s\n",
2127 O2S(value2Ptr), s,
2128 (value2Ptr->typePtr?
2129 value2Ptr->typePtr->name : "null")));
2130 IllegalExprOperandType(interp, pc, value2Ptr);
2131 Tcl_DecrRefCount(valuePtr);
2132 Tcl_DecrRefCount(value2Ptr);
2133 goto checkForCatch;
2134 }
2135 t2Ptr = value2Ptr->typePtr;
2136 }
2137
2138 if ((t1Ptr == &tclDoubleType) || (t2Ptr == &tclDoubleType)) {
2139 /*
2140 * Do double arithmetic.
2141 */
2142 doDouble = 1;
2143 if (t1Ptr == &tclIntType) {
2144 d1 = i; /* promote value 1 to double */
2145 } else if (t2Ptr == &tclIntType) {
2146 d2 = i2; /* promote value 2 to double */
2147 }
2148 switch (*pc) {
2149 case INST_ADD:
2150 dResult = d1 + d2;
2151 break;
2152 case INST_SUB:
2153 dResult = d1 - d2;
2154 break;
2155 case INST_MULT:
2156 dResult = d1 * d2;
2157 break;
2158 case INST_DIV:
2159 if (d2 == 0.0) {
2160 TRACE(("%.6g %.6g => DIVIDE BY ZERO\n", d1, d2));
2161 Tcl_DecrRefCount(valuePtr);
2162 Tcl_DecrRefCount(value2Ptr);
2163 goto divideByZero;
2164 }
2165 dResult = d1 / d2;
2166 break;
2167 }
2168
2169 /*
2170 * Check now for IEEE floating-point error.
2171 */
2172
2173 if (IS_NAN(dResult) || IS_INF(dResult)) {
2174 TRACE(("%.20s %.20s => IEEE FLOATING PT ERROR\n",
2175 O2S(valuePtr), O2S(value2Ptr)));
2176 TclExprFloatError(interp, dResult);
2177 result = TCL_ERROR;
2178 Tcl_DecrRefCount(valuePtr);
2179 Tcl_DecrRefCount(value2Ptr);
2180 goto checkForCatch;
2181 }
2182 } else {
2183 /*
2184 * Do integer arithmetic.
2185 */
2186 switch (*pc) {
2187 case INST_ADD:
2188 iResult = i + i2;
2189 break;
2190 case INST_SUB:
2191 iResult = i - i2;
2192 break;
2193 case INST_MULT:
2194 iResult = i * i2;
2195 break;
2196 case INST_DIV:
2197 /*
2198 * This code is tricky: C doesn't guarantee much
2199 * about the quotient or remainder, but Tcl does.
2200 * The remainder always has the same sign as the
2201 * divisor and a smaller absolute value.
2202 */
2203 if (i2 == 0) {
2204 TRACE(("%ld %ld => DIVIDE BY ZERO\n", i, i2));
2205 Tcl_DecrRefCount(valuePtr);
2206 Tcl_DecrRefCount(value2Ptr);
2207 goto divideByZero;
2208 }
2209 if (i2 < 0) {
2210 i2 = -i2;
2211 i = -i;
2212 }
2213 quot = i / i2;
2214 rem = i % i2;
2215 if (rem < 0) {
2216 quot -= 1;
2217 }
2218 iResult = quot;
2219 break;
2220 }
2221 }
2222
2223 /*
2224 * Reuse the valuePtr object already on stack if possible.
2225 */
2226
2227 if (Tcl_IsShared(valuePtr)) {
2228 if (doDouble) {
2229 PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
2230 TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult));
2231 } else {
2232 PUSH_OBJECT(Tcl_NewLongObj(iResult));
2233 TRACE(("%ld %ld => %ld\n", i, i2, iResult));
2234 }
2235 TclDecrRefCount(valuePtr);
2236 } else { /* reuse the valuePtr object */
2237 if (doDouble) { /* NB: stack top is off by 1 */
2238 TRACE(("%.6g %.6g => %.6g\n", d1, d2, dResult));
2239 Tcl_SetDoubleObj(valuePtr, dResult);
2240 } else {
2241 TRACE(("%ld %ld => %ld\n", i, i2, iResult));
2242 Tcl_SetLongObj(valuePtr, iResult);
2243 }
2244 ++stackTop; /* valuePtr now on stk top has right r.c. */
2245 }
2246 TclDecrRefCount(value2Ptr);
2247 }
2248 ADJUST_PC(1);
2249
2250 case INST_UPLUS:
2251 {
2252 /*
2253 * Operand must be numeric.
2254 */
2255
2256 double d;
2257 Tcl_ObjType *tPtr;
2258
2259 valuePtr = stackPtr[stackTop];
2260 tPtr = valuePtr->typePtr;
2261 if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType)
2262 || (valuePtr->bytes != NULL))) {
2263 char *s = Tcl_GetStringFromObj(valuePtr, &length);
2264 if (TclLooksLikeInt(s, length)) {
2265 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
2266 valuePtr, &i);
2267 } else {
2268 result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
2269 valuePtr, &d);
2270 }
2271 if (result != TCL_OK) {
2272 TRACE(("\"%.20s\" => ILLEGAL TYPE %s \n",
2273 s, (tPtr? tPtr->name : "null")));
2274 IllegalExprOperandType(interp, pc, valuePtr);
2275 goto checkForCatch;
2276 }
2277 tPtr = valuePtr->typePtr;
2278 }
2279
2280 /*
2281 * Ensure that the operand's string rep is the same as the
2282 * formatted version of its internal rep. This makes sure
2283 * that "expr +000123" yields "83", not "000123". We
2284 * implement this by _discarding_ the string rep since we
2285 * know it will be regenerated, if needed later, by
2286 * formatting the internal rep's value.
2287 */
2288
2289 if (Tcl_IsShared(valuePtr)) {
2290 if (tPtr == &tclIntType) {
2291 i = valuePtr->internalRep.longValue;
2292 objPtr = Tcl_NewLongObj(i);
2293 } else {
2294 d = valuePtr->internalRep.doubleValue;
2295 objPtr = Tcl_NewDoubleObj(d);
2296 }
2297 Tcl_IncrRefCount(objPtr);
2298 Tcl_DecrRefCount(valuePtr);
2299 valuePtr = objPtr;
2300 stackPtr[stackTop] = valuePtr;
2301 } else {
2302 Tcl_InvalidateStringRep(valuePtr);
2303 }
2304 TRACE_WITH_OBJ(("%s => ", O2S(valuePtr)), valuePtr);
2305 }
2306 ADJUST_PC(1);
2307
2308 case INST_UMINUS:
2309 case INST_LNOT:
2310 {
2311 /*
2312 * The operand must be numeric. If the operand object is
2313 * unshared modify it directly, otherwise create a copy to
2314 * modify: this is "copy on write". free any old string
2315 * representation since it is now invalid.
2316 */
2317
2318 double d;
2319 Tcl_ObjType *tPtr;
2320
2321 valuePtr = POP_OBJECT();
2322 tPtr = valuePtr->typePtr;
2323 if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType)
2324 || (valuePtr->bytes != NULL))) {
2325 if ((tPtr == &tclBooleanType)
2326 && (valuePtr->bytes == NULL)) {
2327 valuePtr->typePtr = &tclIntType;
2328 } else {
2329 char *s = Tcl_GetStringFromObj(valuePtr, &length);
2330 if (TclLooksLikeInt(s, length)) {
2331 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
2332 valuePtr, &i);
2333 } else {
2334 result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
2335 valuePtr, &d);
2336 }
2337 if (result != TCL_OK) {
2338 TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n",
2339 s, (tPtr? tPtr->name : "null")));
2340 IllegalExprOperandType(interp, pc, valuePtr);
2341 Tcl_DecrRefCount(valuePtr);
2342 goto checkForCatch;
2343 }
2344 }
2345 tPtr = valuePtr->typePtr;
2346 }
2347
2348 if (Tcl_IsShared(valuePtr)) {
2349 /*
2350 * Create a new object.
2351 */
2352 if (tPtr == &tclIntType) {
2353 i = valuePtr->internalRep.longValue;
2354 objPtr = Tcl_NewLongObj(
2355 (*pc == INST_UMINUS)? -i : !i);
2356 TRACE_WITH_OBJ(("%ld => ", i), objPtr);
2357 } else {
2358 d = valuePtr->internalRep.doubleValue;
2359 if (*pc == INST_UMINUS) {
2360 objPtr = Tcl_NewDoubleObj(-d);
2361 } else {
2362 /*
2363 * Should be able to use "!d", but apparently
2364 * some compilers can't handle it.
2365 */
2366 objPtr = Tcl_NewLongObj((d==0.0)? 1 : 0);
2367 }
2368 TRACE_WITH_OBJ(("%.6g => ", d), objPtr);
2369 }
2370 PUSH_OBJECT(objPtr);
2371 TclDecrRefCount(valuePtr);
2372 } else {
2373 /*
2374 * valuePtr is unshared. Modify it directly.
2375 */
2376 if (tPtr == &tclIntType) {
2377 i = valuePtr->internalRep.longValue;
2378 Tcl_SetLongObj(valuePtr,
2379 (*pc == INST_UMINUS)? -i : !i);
2380 TRACE_WITH_OBJ(("%ld => ", i), valuePtr);
2381 } else {
2382 d = valuePtr->internalRep.doubleValue;
2383 if (*pc == INST_UMINUS) {
2384 Tcl_SetDoubleObj(valuePtr, -d);
2385 } else {
2386 /*
2387 * Should be able to use "!d", but apparently
2388 * some compilers can't handle it.
2389 */
2390 Tcl_SetLongObj(valuePtr, (d==0.0)? 1 : 0);
2391 }
2392 TRACE_WITH_OBJ(("%.6g => ", d), valuePtr);
2393 }
2394 ++stackTop; /* valuePtr now on stk top has right r.c. */
2395 }
2396 }
2397 ADJUST_PC(1);
2398
2399 case INST_BITNOT:
2400 {
2401 /*
2402 * The operand must be an integer. If the operand object is
2403 * unshared modify it directly, otherwise modify a copy.
2404 * Free any old string representation since it is now
2405 * invalid.
2406 */
2407
2408 Tcl_ObjType *tPtr;
2409
2410 valuePtr = POP_OBJECT();
2411 tPtr = valuePtr->typePtr;
2412 if (tPtr != &tclIntType) {
2413 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
2414 valuePtr, &i);
2415 if (result != TCL_OK) { /* try to convert to double */
2416 TRACE(("\"%.20s\" => ILLEGAL TYPE %s\n",
2417 O2S(valuePtr), (tPtr? tPtr->name : "null")));
2418 IllegalExprOperandType(interp, pc, valuePtr);
2419 Tcl_DecrRefCount(valuePtr);
2420 goto checkForCatch;
2421 }
2422 }
2423
2424 i = valuePtr->internalRep.longValue;
2425 if (Tcl_IsShared(valuePtr)) {
2426 PUSH_OBJECT(Tcl_NewLongObj(~i));
2427 TRACE(("0x%lx => (%lu)\n", i, ~i));
2428 TclDecrRefCount(valuePtr);
2429 } else {
2430 /*
2431 * valuePtr is unshared. Modify it directly.
2432 */
2433 Tcl_SetLongObj(valuePtr, ~i);
2434 ++stackTop; /* valuePtr now on stk top has right r.c. */
2435 TRACE(("0x%lx => (%lu)\n", i, ~i));
2436 }
2437 }
2438 ADJUST_PC(1);
2439
2440 case INST_CALL_BUILTIN_FUNC1:
2441 opnd = TclGetUInt1AtPtr(pc+1);
2442 {
2443 /*
2444 * Call one of the built-in Tcl math functions.
2445 */
2446
2447 BuiltinFunc *mathFuncPtr;
2448 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2449
2450 if ((opnd < 0) || (opnd > LAST_BUILTIN_FUNC)) {
2451 TRACE(("UNRECOGNIZED BUILTIN FUNC CODE %d\n", opnd));
2452 panic("TclExecuteByteCode: unrecognized builtin function code %d", opnd);
2453 }
2454 mathFuncPtr = &(builtinFuncTable[opnd]);
2455 DECACHE_STACK_INFO();
2456 tsdPtr->mathInProgress++;
2457 result = (*mathFuncPtr->proc)(interp, eePtr,
2458 mathFuncPtr->clientData);
2459 tsdPtr->mathInProgress--;
2460 CACHE_STACK_INFO();
2461 if (result != TCL_OK) {
2462 goto checkForCatch;
2463 }
2464 TRACE_WITH_OBJ(("%d => ", opnd), stackPtr[stackTop]);
2465 }
2466 ADJUST_PC(2);
2467
2468 case INST_CALL_FUNC1:
2469 opnd = TclGetUInt1AtPtr(pc+1);
2470 {
2471 /*
2472 * Call a non-builtin Tcl math function previously
2473 * registered by a call to Tcl_CreateMathFunc.
2474 */
2475
2476 int objc = opnd; /* Number of arguments. The function name
2477 * is the 0-th argument. */
2478 Tcl_Obj **objv; /* The array of arguments. The function
2479 * name is objv[0]. */
2480 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
2481
2482 objv = &(stackPtr[stackTop - (objc-1)]); /* "objv[0]" */
2483 DECACHE_STACK_INFO();
2484 tsdPtr->mathInProgress++;
2485 result = ExprCallMathFunc(interp, eePtr, objc, objv);
2486 tsdPtr->mathInProgress--;
2487 CACHE_STACK_INFO();
2488 if (result != TCL_OK) {
2489 goto checkForCatch;
2490 }
2491 TRACE_WITH_OBJ(("%d => ", objc), stackPtr[stackTop]);
2492 ADJUST_PC(2);
2493 }
2494
2495 case INST_TRY_CVT_TO_NUMERIC:
2496 {
2497 /*
2498 * Try to convert the topmost stack object to an int or
2499 * double object. This is done in order to support Tcl's
2500 * policy of interpreting operands if at all possible as
2501 * first integers, else floating-point numbers.
2502 */
2503
2504 double d;
2505 char *s;
2506 Tcl_ObjType *tPtr;
2507 int converted, shared;
2508
2509 valuePtr = stackPtr[stackTop];
2510 tPtr = valuePtr->typePtr;
2511 converted = 0;
2512 if ((tPtr != &tclIntType) && ((tPtr != &tclDoubleType)
2513 || (valuePtr->bytes != NULL))) {
2514 if ((tPtr == &tclBooleanType)
2515 && (valuePtr->bytes == NULL)) {
2516 valuePtr->typePtr = &tclIntType;
2517 converted = 1;
2518 } else {
2519 s = Tcl_GetStringFromObj(valuePtr, &length);
2520 if (TclLooksLikeInt(s, length)) {
2521 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL,
2522 valuePtr, &i);
2523 } else {
2524 result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL,
2525 valuePtr, &d);
2526 }
2527 if (result == TCL_OK) {
2528 converted = 1;
2529 }
2530 result = TCL_OK; /* reset the result variable */
2531 }
2532 tPtr = valuePtr->typePtr;
2533 }
2534
2535 /*
2536 * Ensure that the topmost stack object, if numeric, has a
2537 * string rep the same as the formatted version of its
2538 * internal rep. This is used, e.g., to make sure that "expr
2539 * {0001}" yields "1", not "0001". We implement this by
2540 * _discarding_ the string rep since we know it will be
2541 * regenerated, if needed later, by formatting the internal
2542 * rep's value. Also check if there has been an IEEE
2543 * floating point error.
2544 */
2545
2546 if ((tPtr == &tclIntType) || (tPtr == &tclDoubleType)) {
2547 shared = 0;
2548 if (Tcl_IsShared(valuePtr)) {
2549 shared = 1;
2550 if (valuePtr->bytes != NULL) {
2551 /*
2552 * We only need to make a copy of the object
2553 * when it already had a string rep
2554 */
2555 if (tPtr == &tclIntType) {
2556 i = valuePtr->internalRep.longValue;
2557 objPtr = Tcl_NewLongObj(i);
2558 } else {
2559 d = valuePtr->internalRep.doubleValue;
2560 objPtr = Tcl_NewDoubleObj(d);
2561 }
2562 Tcl_IncrRefCount(objPtr);
2563 TclDecrRefCount(valuePtr);
2564 valuePtr = objPtr;
2565 stackPtr[stackTop] = valuePtr;
2566 tPtr = valuePtr->typePtr;
2567 }
2568 } else {
2569 Tcl_InvalidateStringRep(valuePtr);
2570 }
2571
2572 if (tPtr == &tclDoubleType) {
2573 d = valuePtr->internalRep.doubleValue;
2574 if (IS_NAN(d) || IS_INF(d)) {
2575 TRACE(("\"%.20s\" => IEEE FLOATING PT ERROR\n",
2576 O2S(valuePtr)));
2577 TclExprFloatError(interp, d);
2578 result = TCL_ERROR;
2579 goto checkForCatch;
2580 }
2581 }
2582 shared = shared; /* lint, shared not used. */
2583 converted = converted; /* lint, converted not used. */
2584 TRACE(("\"%.20s\" => numeric, %s, %s\n", O2S(valuePtr),
2585 (converted? "converted" : "not converted"),
2586 (shared? "shared" : "not shared")));
2587 } else {
2588 TRACE(("\"%.20s\" => not numeric\n", O2S(valuePtr)));
2589 }
2590 }
2591 ADJUST_PC(1);
2592
2593 case INST_BREAK:
2594 /*
2595 * First reset the interpreter's result. Then find the closest
2596 * enclosing loop or catch exception range, if any. If a loop is
2597 * found, terminate its execution. If the closest is a catch
2598 * exception range, jump to its catchOffset. If no enclosing
2599 * range is found, stop execution and return TCL_BREAK.
2600 */
2601
2602 Tcl_ResetResult(interp);
2603 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr);
2604 if (rangePtr == NULL) {
2605 TRACE(("=> no encl. loop or catch, returning TCL_BREAK\n"));
2606 result = TCL_BREAK;
2607 goto abnormalReturn; /* no catch exists to check */
2608 }
2609 switch (rangePtr->type) {
2610 case LOOP_EXCEPTION_RANGE:
2611 result = TCL_OK;
2612 TRACE(("=> range at %d, new pc %d\n",
2613 rangePtr->codeOffset, rangePtr->breakOffset));
2614 break;
2615 case CATCH_EXCEPTION_RANGE:
2616 result = TCL_BREAK;
2617 TRACE(("=> ...\n"));
2618 goto processCatch; /* it will use rangePtr */
2619 default:
2620 panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);
2621 }
2622 pc = (codePtr->codeStart + rangePtr->breakOffset);
2623 continue; /* restart outer instruction loop at pc */
2624
2625 case INST_CONTINUE:
2626 /*
2627 * Find the closest enclosing loop or catch exception range,
2628 * if any. If a loop is found, skip to its next iteration.
2629 * If the closest is a catch exception range, jump to its
2630 * catchOffset. If no enclosing range is found, stop
2631 * execution and return TCL_CONTINUE.
2632 */
2633
2634 Tcl_ResetResult(interp);
2635 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr);
2636 if (rangePtr == NULL) {
2637 TRACE(("=> no encl. loop or catch, returning TCL_CONTINUE\n"));
2638 result = TCL_CONTINUE;
2639 goto abnormalReturn;
2640 }
2641 switch (rangePtr->type) {
2642 case LOOP_EXCEPTION_RANGE:
2643 if (rangePtr->continueOffset == -1) {
2644 TRACE(("=> loop w/o continue, checking for catch\n"));
2645 goto checkForCatch;
2646 } else {
2647 result = TCL_OK;
2648 TRACE(("=> range at %d, new pc %d\n",
2649 rangePtr->codeOffset, rangePtr->continueOffset));
2650 }
2651 break;
2652 case CATCH_EXCEPTION_RANGE:
2653 result = TCL_CONTINUE;
2654 TRACE(("=> ...\n"));
2655 goto processCatch; /* it will use rangePtr */
2656 default:
2657 panic("TclExecuteByteCode: unrecognized ExceptionRange type %d\n", rangePtr->type);
2658 }
2659 pc = (codePtr->codeStart + rangePtr->continueOffset);
2660 continue; /* restart outer instruction loop at pc */
2661
2662 case INST_FOREACH_START4:
2663 opnd = TclGetUInt4AtPtr(pc+1);
2664 {
2665 /*
2666 * Initialize the temporary local var that holds the count
2667 * of the number of iterations of the loop body to -1.
2668 */
2669
2670 ForeachInfo *infoPtr = (ForeachInfo *)
2671 codePtr->auxDataArrayPtr[opnd].clientData;
2672 int iterTmpIndex = infoPtr->loopCtTemp;
2673 Var *compiledLocals = iPtr->varFramePtr->compiledLocals;
2674 Var *iterVarPtr = &(compiledLocals[iterTmpIndex]);
2675 Tcl_Obj *oldValuePtr = iterVarPtr->value.objPtr;
2676
2677 if (oldValuePtr == NULL) {
2678 iterVarPtr->value.objPtr = Tcl_NewLongObj(-1);
2679 Tcl_IncrRefCount(iterVarPtr->value.objPtr);
2680 } else {
2681 Tcl_SetLongObj(oldValuePtr, -1);
2682 }
2683 TclSetVarScalar(iterVarPtr);
2684 TclClearVarUndefined(iterVarPtr);
2685 TRACE(("%u => loop iter count temp %d\n",
2686 opnd, iterTmpIndex));
2687 }
2688 ADJUST_PC(5);
2689
2690 case INST_FOREACH_STEP4:
2691 opnd = TclGetUInt4AtPtr(pc+1);
2692 {
2693 /*
2694 * "Step" a foreach loop (i.e., begin its next iteration) by
2695 * assigning the next value list element to each loop var.
2696 */
2697
2698 ForeachInfo *infoPtr = (ForeachInfo *)
2699 codePtr->auxDataArrayPtr[opnd].clientData;
2700 ForeachVarList *varListPtr;
2701 int numLists = infoPtr->numLists;
2702 Var *compiledLocals = iPtr->varFramePtr->compiledLocals;
2703 Tcl_Obj *listPtr;
2704 List *listRepPtr;
2705 Var *iterVarPtr, *listVarPtr;
2706 int iterNum, listTmpIndex, listLen, numVars;
2707 int varIndex, valIndex, continueLoop, j;
2708
2709 /*
2710 * Increment the temp holding the loop iteration number.
2711 */
2712
2713 iterVarPtr = &(compiledLocals[infoPtr->loopCtTemp]);
2714 valuePtr = iterVarPtr->value.objPtr;
2715 iterNum = (valuePtr->internalRep.longValue + 1);
2716 Tcl_SetLongObj(valuePtr, iterNum);
2717
2718 /*
2719 * Check whether all value lists are exhausted and we should
2720 * stop the loop.
2721 */
2722
2723 continueLoop = 0;
2724 listTmpIndex = infoPtr->firstValueTemp;
2725 for (i = 0; i < numLists; i++) {
2726 varListPtr = infoPtr->varLists[i];
2727 numVars = varListPtr->numVars;
2728
2729 listVarPtr = &(compiledLocals[listTmpIndex]);
2730 listPtr = listVarPtr->value.objPtr;
2731 result = Tcl_ListObjLength(interp, listPtr, &listLen);
2732 if (result != TCL_OK) {
2733 TRACE_WITH_OBJ(("%u => ERROR converting list %ld, \"%s\": ",
2734 opnd, i, O2S(listPtr)),
2735 Tcl_GetObjResult(interp));
2736 goto checkForCatch;
2737 }
2738 if (listLen > (iterNum * numVars)) {
2739 continueLoop = 1;
2740 }
2741 listTmpIndex++;
2742 }
2743
2744 /*
2745 * If some var in some var list still has a remaining list
2746 * element iterate one more time. Assign to var the next
2747 * element from its value list. We already checked above
2748 * that each list temp holds a valid list object.
2749 */
2750
2751 if (continueLoop) {
2752 listTmpIndex = infoPtr->firstValueTemp;
2753 for (i = 0; i < numLists; i++) {
2754 varListPtr = infoPtr->varLists[i];
2755 numVars = varListPtr->numVars;
2756
2757 listVarPtr = &(compiledLocals[listTmpIndex]);
2758 listPtr = listVarPtr->value.objPtr;
2759 listRepPtr = (List *) listPtr->internalRep.otherValuePtr;
2760 listLen = listRepPtr->elemCount;
2761
2762 valIndex = (iterNum * numVars);
2763 for (j = 0; j < numVars; j++) {
2764 int setEmptyStr = 0;
2765 if (valIndex >= listLen) {
2766 setEmptyStr = 1;
2767 valuePtr = Tcl_NewObj();
2768 } else {
2769 valuePtr = listRepPtr->elements[valIndex];
2770 }
2771
2772 varIndex = varListPtr->varIndexes[j];
2773 DECACHE_STACK_INFO();
2774 value2Ptr = TclSetIndexedScalar(interp,
2775 varIndex, valuePtr, /*leaveErrorMsg*/ 1);
2776 CACHE_STACK_INFO();
2777 if (value2Ptr == NULL) {
2778 TRACE_WITH_OBJ(("%u => ERROR init. index temp %d: ",
2779 opnd, varIndex),
2780 Tcl_GetObjResult(interp));
2781 if (setEmptyStr) {
2782 Tcl_DecrRefCount(valuePtr);
2783 }
2784 result = TCL_ERROR;
2785 goto checkForCatch;
2786 }
2787 valIndex++;
2788 }
2789 listTmpIndex++;
2790 }
2791 }
2792
2793 /*
2794 * Push 1 if at least one value list had a remaining element
2795 * and the loop should continue. Otherwise push 0.
2796 */
2797
2798 PUSH_OBJECT(Tcl_NewLongObj(continueLoop));
2799 TRACE(("%u => %d lists, iter %d, %s loop\n",
2800 opnd, numLists, iterNum,
2801 (continueLoop? "continue" : "exit")));
2802 }
2803 ADJUST_PC(5);
2804
2805 case INST_BEGIN_CATCH4:
2806 /*
2807 * Record start of the catch command with exception range index
2808 * equal to the operand. Push the current stack depth onto the
2809 * special catch stack.
2810 */
2811 catchStackPtr[++catchTop] = stackTop;
2812 TRACE(("%u => catchTop=%d, stackTop=%d\n",
2813 TclGetUInt4AtPtr(pc+1), catchTop, stackTop));
2814 ADJUST_PC(5);
2815
2816 case INST_END_CATCH:
2817 catchTop--;
2818 result = TCL_OK;
2819 TRACE(("=> catchTop=%d\n", catchTop));
2820 ADJUST_PC(1);
2821
2822 case INST_PUSH_RESULT:
2823 PUSH_OBJECT(Tcl_GetObjResult(interp));
2824 TRACE_WITH_OBJ(("=> "), Tcl_GetObjResult(interp));
2825 ADJUST_PC(1);
2826
2827 case INST_PUSH_RETURN_CODE:
2828 PUSH_OBJECT(Tcl_NewLongObj(result));
2829 TRACE(("=> %u\n", result));
2830 ADJUST_PC(1);
2831
2832 default:
2833 panic("TclExecuteByteCode: unrecognized opCode %u", *pc);
2834 } /* end of switch on opCode */
2835
2836 /*
2837 * Division by zero in an expression. Control only reaches this
2838 * point by "goto divideByZero".
2839 */
2840
2841 divideByZero:
2842 Tcl_ResetResult(interp);
2843 Tcl_AppendToObj(Tcl_GetObjResult(interp), "divide by zero", -1);
2844 Tcl_SetErrorCode(interp, "ARITH", "DIVZERO", "divide by zero",
2845 (char *) NULL);
2846 result = TCL_ERROR;
2847
2848 /*
2849 * Execution has generated an "exception" such as TCL_ERROR. If the
2850 * exception is an error, record information about what was being
2851 * executed when the error occurred. Find the closest enclosing
2852 * catch range, if any. If no enclosing catch range is found, stop
2853 * execution and return the "exception" code.
2854 */
2855
2856 checkForCatch:
2857 if ((result == TCL_ERROR) && !(iPtr->flags & ERR_ALREADY_LOGGED)) {
2858 bytes = GetSrcInfoForPc(pc, codePtr, &length);
2859 if (bytes != NULL) {
2860 Tcl_LogCommandInfo(interp, codePtr->source, bytes, length);
2861 iPtr->flags |= ERR_ALREADY_LOGGED;
2862 }
2863 }
2864 rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr);
2865 if (rangePtr == NULL) {
2866 #ifdef TCL_COMPILE_DEBUG
2867 if (traceInstructions) {
2868 fprintf(stdout, " ... no enclosing catch, returning %s\n",
2869 StringForResultCode(result));
2870 }
2871 #endif
2872 goto abnormalReturn;
2873 }
2874
2875 /*
2876 * A catch exception range (rangePtr) was found to handle an
2877 * "exception". It was found either by checkForCatch just above or
2878 * by an instruction during break, continue, or error processing.
2879 * Jump to its catchOffset after unwinding the operand stack to
2880 * the depth it had when starting to execute the range's catch
2881 * command.
2882 */
2883
2884 processCatch:
2885 while (stackTop > catchStackPtr[catchTop]) {
2886 valuePtr = POP_OBJECT();
2887 TclDecrRefCount(valuePtr);
2888 }
2889 #ifdef TCL_COMPILE_DEBUG
2890 if (traceInstructions) {
2891 fprintf(stdout, " ... found catch at %d, catchTop=%d, unwound to %d, new pc %u\n",
2892 rangePtr->codeOffset, catchTop, catchStackPtr[catchTop],
2893 (unsigned int)(rangePtr->catchOffset));
2894 }
2895 #endif
2896 pc = (codePtr->codeStart + rangePtr->catchOffset);
2897 continue; /* restart the execution loop at pc */
2898 } /* end of infinite loop dispatching on instructions */
2899
2900 /*
2901 * Abnormal return code. Restore the stack to state it had when starting
2902 * to execute the ByteCode.
2903 */
2904
2905 abnormalReturn:
2906 while (stackTop > initStackTop) {
2907 valuePtr = POP_OBJECT();
2908 Tcl_DecrRefCount(valuePtr);
2909 }
2910
2911 /*
2912 * Free the catch stack array if malloc'ed storage was used.
2913 */
2914
2915 done:
2916 if (catchStackPtr != catchStackStorage) {
2917 ckfree((char *) catchStackPtr);
2918 }
2919 eePtr->stackTop = initStackTop;
2920 return result;
2921 #undef STATIC_CATCH_STACK_SIZE
2922 }
2923
2924 #ifdef TCL_COMPILE_DEBUG
2925 /*
2926 *----------------------------------------------------------------------
2927 *
2928 * PrintByteCodeInfo --
2929 *
2930 * This procedure prints a summary about a bytecode object to stdout.
2931 * It is called by TclExecuteByteCode when starting to execute the
2932 * bytecode object if tclTraceExec has the value 2 or more.
2933 *
2934 * Results:
2935 * None.
2936 *
2937 * Side effects:
2938 * None.
2939 *
2940 *----------------------------------------------------------------------
2941 */
2942
2943 static void
2944 PrintByteCodeInfo(codePtr)
2945 register ByteCode *codePtr; /* The bytecode whose summary is printed
2946 * to stdout. */
2947 {
2948 Proc *procPtr = codePtr->procPtr;
2949 Interp *iPtr = (Interp *) *codePtr->interpHandle;
2950
2951 fprintf(stdout, "\nExecuting ByteCode 0x%x, refCt %u, epoch %u, interp 0x%x (epoch %u)\n",
2952 (unsigned int) codePtr, codePtr->refCount,
2953 codePtr->compileEpoch, (unsigned int) iPtr,
2954 iPtr->compileEpoch);
2955
2956 fprintf(stdout, " Source: ");
2957 TclPrintSource(stdout, codePtr->source, 60);
2958
2959 fprintf(stdout, "\n Cmds %d, src %d, inst %u, litObjs %u, aux %d, stkDepth %u, code/src %.2f\n",
2960 codePtr->numCommands, codePtr->numSrcBytes,
2961 codePtr->numCodeBytes, codePtr->numLitObjects,
2962 codePtr->numAuxDataItems, codePtr->maxStackDepth,
2963 #ifdef TCL_COMPILE_STATS
2964 (codePtr->numSrcBytes?
2965 ((float)codePtr->structureSize)/((float)codePtr->numSrcBytes) : 0.0));
2966 #else
2967 0.0);
2968 #endif
2969 #ifdef TCL_COMPILE_STATS
2970 fprintf(stdout, " Code %d = header %d+inst %d+litObj %d+exc %d+aux %d+cmdMap %d\n",
2971 codePtr->structureSize,
2972 (sizeof(ByteCode) - (sizeof(size_t) + sizeof(Tcl_Time))),
2973 codePtr->numCodeBytes,
2974 (codePtr->numLitObjects * sizeof(Tcl_Obj *)),
2975 (codePtr->numExceptRanges * sizeof(ExceptionRange)),
2976 (codePtr->numAuxDataItems * sizeof(AuxData)),
2977 codePtr->numCmdLocBytes);
2978 #endif /* TCL_COMPILE_STATS */
2979 if (procPtr != NULL) {
2980 fprintf(stdout,
2981 " Proc 0x%x, refCt %d, args %d, compiled locals %d\n",
2982 (unsigned int) procPtr, procPtr->refCount,
2983 procPtr->numArgs, procPtr->numCompiledLocals);
2984 }
2985 }
2986 #endif /* TCL_COMPILE_DEBUG */
2987
2988 /*
2989 *----------------------------------------------------------------------
2990 *
2991 * ValidatePcAndStackTop --
2992 *
2993 * This procedure is called by TclExecuteByteCode when debugging to
2994 * verify that the program counter and stack top are valid during
2995 * execution.
2996 *
2997 * Results:
2998 * None.
2999 *
3000 * Side effects:
3001 * Prints a message to stderr and panics if either the pc or stack
3002 * top are invalid.
3003 *
3004 *----------------------------------------------------------------------
3005 */
3006
3007 #ifdef TCL_COMPILE_DEBUG
3008 static void
3009 ValidatePcAndStackTop(codePtr, pc, stackTop, stackLowerBound,
3010 stackUpperBound)
3011 register ByteCode *codePtr; /* The bytecode whose summary is printed
3012 * to stdout. */
3013 unsigned char *pc; /* Points to first byte of a bytecode
3014 * instruction. The program counter. */
3015 int stackTop; /* Current stack top. Must be between
3016 * stackLowerBound and stackUpperBound
3017 * (inclusive). */
3018 int stackLowerBound; /* Smallest legal value for stackTop. */
3019 int stackUpperBound; /* Greatest legal value for stackTop. */
3020 {
3021 unsigned int relativePc = (unsigned int) (pc - codePtr->codeStart);
3022 unsigned int codeStart = (unsigned int) codePtr->codeStart;
3023 unsigned int codeEnd = (unsigned int)
3024 (codePtr->codeStart + codePtr->numCodeBytes);
3025 unsigned char opCode = *pc;
3026
3027 if (((unsigned int) pc < codeStart) || ((unsigned int) pc > codeEnd)) {
3028 fprintf(stderr, "\nBad instruction pc 0x%x in TclExecuteByteCode\n",
3029 (unsigned int) pc);
3030 panic("TclExecuteByteCode execution failure: bad pc");
3031 }
3032 if ((unsigned int) opCode > LAST_INST_OPCODE) {
3033 fprintf(stderr, "\nBad opcode %d at pc %u in TclExecuteByteCode\n",
3034 (unsigned int) opCode, relativePc);
3035 panic("TclExecuteByteCode execution failure: bad opcode");
3036 }
3037 if ((stackTop < stackLowerBound) || (stackTop > stackUpperBound)) {
3038 int numChars;
3039 char *cmd = GetSrcInfoForPc(pc, codePtr, &numChars);
3040 char *ellipsis = "";
3041
3042 fprintf(stderr, "\nBad stack top %d at pc %u in TclExecuteByteCode",
3043 stackTop, relativePc);
3044 if (cmd != NULL) {
3045 if (numChars > 100) {
3046 numChars = 100;
3047 ellipsis = "...";
3048 }
3049 fprintf(stderr, "\n executing %.*s%s\n", numChars, cmd,
3050 ellipsis);
3051 } else {
3052 fprintf(stderr, "\n");
3053 }
3054 panic("TclExecuteByteCode execution failure: bad stack top");
3055 }
3056 }
3057 #endif /* TCL_COMPILE_DEBUG */
3058
3059 /*
3060 *----------------------------------------------------------------------
3061 *
3062 * IllegalExprOperandType --
3063 *
3064 * Used by TclExecuteByteCode to add an error message to errorInfo
3065 * when an illegal operand type is detected by an expression
3066 * instruction. The argument opndPtr holds the operand object in error.
3067 *
3068 * Results:
3069 * None.
3070 *
3071 * Side effects:
3072 * An error message is appended to errorInfo.
3073 *
3074 *----------------------------------------------------------------------
3075 */
3076
3077 static void
3078 IllegalExprOperandType(interp, pc, opndPtr)
3079 Tcl_Interp *interp; /* Interpreter to which error information
3080 * pertains. */
3081 unsigned char *pc; /* Points to the instruction being executed
3082 * when the illegal type was found. */
3083 Tcl_Obj *opndPtr; /* Points to the operand holding the value
3084 * with the illegal type. */
3085 {
3086 unsigned char opCode = *pc;
3087
3088 Tcl_ResetResult(interp);
3089 if ((opndPtr->bytes == NULL) || (opndPtr->length == 0)) {
3090 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
3091 "can't use empty string as operand of \"",
3092 operatorStrings[opCode - INST_LOR], "\"", (char *) NULL);
3093 } else {
3094 char *msg = "non-numeric string";
3095 if (opndPtr->typePtr != &tclDoubleType) {
3096 /*
3097 * See if the operand can be interpreted as a double in order to
3098 * improve the error message.
3099 */
3100
3101 char *s = Tcl_GetString(opndPtr);
3102 double d;
3103
3104 if (Tcl_GetDouble((Tcl_Interp *) NULL, s, &d) == TCL_OK) {
3105 /*
3106 * Make sure that what appears to be a double
3107 * (ie 08) isn't really a bad octal
3108 */
3109 if (TclCheckBadOctal(NULL, Tcl_GetString(opndPtr))) {
3110 msg = "invalid octal number";
3111 } else {
3112 msg = "floating-point value";
3113 }
3114 }
3115 }
3116 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), "can't use ",
3117 msg, " as operand of \"", operatorStrings[opCode - INST_LOR],
3118 "\"", (char *) NULL);
3119 }
3120 }
3121
3122 /*
3123 *----------------------------------------------------------------------
3124 *
3125 * CallTraceProcedure --
3126 *
3127 * Invokes a trace procedure registered with an interpreter. These
3128 * procedures trace command execution. Currently this trace procedure
3129 * is called with the address of the string-based Tcl_CmdProc for the
3130 * command, not the Tcl_ObjCmdProc.
3131 *
3132 * Results:
3133 * None.
3134 *
3135 * Side effects:
3136 * Those side effects made by the trace procedure.
3137 *
3138 *----------------------------------------------------------------------
3139 */
3140
3141 static void
3142 CallTraceProcedure(interp, tracePtr, cmdPtr, command, numChars, objc, objv)
3143 Tcl_Interp *interp; /* The current interpreter. */
3144 register Trace *tracePtr; /* Describes the trace procedure to call. */
3145 Command *cmdPtr; /* Points to command's Command struct. */
3146 char *command; /* Points to the first character of the
3147 * command's source before substitutions. */
3148 int numChars; /* The number of characters in the
3149 * command's source. */
3150 register int objc; /* Number of arguments for the command. */
3151 Tcl_Obj *objv[]; /* Pointers to Tcl_Obj of each argument. */
3152 {
3153 Interp *iPtr = (Interp *) interp;
3154 register char **argv;
3155 register int i;
3156 int length;
3157 char *p;
3158
3159 /*
3160 * Get the string rep from the objv argument objects and place their
3161 * pointers in argv. First make sure argv is large enough to hold the
3162 * objc args plus 1 extra word for the zero end-of-argv word.
3163 */
3164
3165 argv = (char **) ckalloc((unsigned)(objc + 1) * sizeof(char *));
3166 for (i = 0; i < objc; i++) {
3167 argv[i] = Tcl_GetStringFromObj(objv[i], &length);
3168 }
3169 argv[objc] = 0;
3170
3171 /*
3172 * Copy the command characters into a new string.
3173 */
3174
3175 p = (char *) ckalloc((unsigned) (numChars + 1));
3176 memcpy((VOID *) p, (VOID *) command, (size_t) numChars);
3177 p[numChars] = '\0';
3178
3179 /*
3180 * Call the trace procedure then free allocated storage.
3181 */
3182
3183 (*tracePtr->proc)(tracePtr->clientData, interp, iPtr->numLevels,
3184 p, cmdPtr->proc, cmdPtr->clientData, objc, argv);
3185
3186 ckfree((char *) argv);
3187 ckfree((char *) p);
3188 }
3189
3190 /*
3191 *----------------------------------------------------------------------
3192 *
3193 * GetSrcInfoForPc --
3194 *
3195 * Given a program counter value, finds the closest command in the
3196 * bytecode code unit's CmdLocation array and returns information about
3197 * that command's source: a pointer to its first byte and the number of
3198 * characters.
3199 *
3200 * Results:
3201 * If a command is found that encloses the program counter value, a
3202 * pointer to the command's source is returned and the length of the
3203 * source is stored at *lengthPtr. If multiple commands resulted in
3204 * code at pc, information about the closest enclosing command is
3205 * returned. If no matching command is found, NULL is returned and
3206 * *lengthPtr is unchanged.
3207 *
3208 * Side effects:
3209 * None.
3210 *
3211 *----------------------------------------------------------------------
3212 */
3213
3214 static char *
3215 GetSrcInfoForPc(pc, codePtr, lengthPtr)
3216 unsigned char *pc; /* The program counter value for which to
3217 * return the closest command's source info.
3218 * This points to a bytecode instruction
3219 * in codePtr's code. */
3220 ByteCode *codePtr; /* The bytecode sequence in which to look
3221 * up the command source for the pc. */
3222 int *lengthPtr; /* If non-NULL, the location where the
3223 * length of the command's source should be
3224 * stored. If NULL, no length is stored. */
3225 {
3226 register int pcOffset = (pc - codePtr->codeStart);
3227 int numCmds = codePtr->numCommands;
3228 unsigned char *codeDeltaNext, *codeLengthNext;
3229 unsigned char *srcDeltaNext, *srcLengthNext;
3230 int codeOffset, codeLen, codeEnd, srcOffset, srcLen, delta, i;
3231 int bestDist = INT_MAX; /* Distance of pc to best cmd's start pc. */
3232 int bestSrcOffset = -1; /* Initialized to avoid compiler warning. */
3233 int bestSrcLength = -1; /* Initialized to avoid compiler warning. */
3234
3235 if ((pcOffset < 0) || (pcOffset >= codePtr->numCodeBytes)) {
3236 return NULL;
3237 }
3238
3239 /*
3240 * Decode the code and source offset and length for each command. The
3241 * closest enclosing command is the last one whose code started before
3242 * pcOffset.
3243 */
3244
3245 codeDeltaNext = codePtr->codeDeltaStart;
3246 codeLengthNext = codePtr->codeLengthStart;
3247 srcDeltaNext = codePtr->srcDeltaStart;
3248 srcLengthNext = codePtr->srcLengthStart;
3249 codeOffset = srcOffset = 0;
3250 for (i = 0; i < numCmds; i++) {
3251 if ((unsigned int) (*codeDeltaNext) == (unsigned int) 0xFF) {
3252 codeDeltaNext++;
3253 delta = TclGetInt4AtPtr(codeDeltaNext);
3254 codeDeltaNext += 4;
3255 } else {
3256 delta = TclGetInt1AtPtr(codeDeltaNext);
3257 codeDeltaNext++;
3258 }
3259 codeOffset += delta;
3260
3261 if ((unsigned int) (*codeLengthNext) == (unsigned int) 0xFF) {
3262 codeLengthNext++;
3263 codeLen = TclGetInt4AtPtr(codeLengthNext);
3264 codeLengthNext += 4;
3265 } else {
3266 codeLen = TclGetInt1AtPtr(codeLengthNext);
3267 codeLengthNext++;
3268 }
3269 codeEnd = (codeOffset + codeLen - 1);
3270
3271 if ((unsigned int) (*srcDeltaNext) == (unsigned int) 0xFF) {
3272 srcDeltaNext++;
3273 delta = TclGetInt4AtPtr(srcDeltaNext);
3274 srcDeltaNext += 4;
3275 } else {
3276 delta = TclGetInt1AtPtr(srcDeltaNext);
3277 srcDeltaNext++;
3278 }
3279 srcOffset += delta;
3280
3281 if ((unsigned int) (*srcLengthNext) == (unsigned int) 0xFF) {
3282 srcLengthNext++;
3283 srcLen = TclGetInt4AtPtr(srcLengthNext);
3284 srcLengthNext += 4;
3285 } else {
3286 srcLen = TclGetInt1AtPtr(srcLengthNext);
3287 srcLengthNext++;
3288 }
3289
3290 if (codeOffset > pcOffset) { /* best cmd already found */
3291 break;
3292 } else if (pcOffset <= codeEnd) { /* this cmd's code encloses pc */
3293 int dist = (pcOffset - codeOffset);
3294 if (dist <= bestDist) {
3295 bestDist = dist;
3296 bestSrcOffset = srcOffset;
3297 bestSrcLength = srcLen;
3298 }
3299 }
3300 }
3301
3302 if (bestDist == INT_MAX) {
3303 return NULL;
3304 }
3305
3306 if (lengthPtr != NULL) {
3307 *lengthPtr = bestSrcLength;
3308 }
3309 return (codePtr->source + bestSrcOffset);
3310 }
3311
3312 /*
3313 *----------------------------------------------------------------------
3314 *
3315 * GetExceptRangeForPc --
3316 *
3317 * Given a program counter value, return the closest enclosing
3318 * ExceptionRange.
3319 *
3320 * Results:
3321 * In the normal case, catchOnly is 0 (false) and this procedure
3322 * returns a pointer to the most closely enclosing ExceptionRange
3323 * structure regardless of whether it is a loop or catch exception
3324 * range. This is appropriate when processing a TCL_BREAK or
3325 * TCL_CONTINUE, which will be "handled" either by a loop exception
3326 * range or a closer catch range. If catchOnly is nonzero, this
3327 * procedure ignores loop exception ranges and returns a pointer to the
3328 * closest catch range. If no matching ExceptionRange is found that
3329 * encloses pc, a NULL is returned.
3330 *
3331 * Side effects:
3332 * None.
3333 *
3334 *----------------------------------------------------------------------
3335 */
3336
3337 static ExceptionRange *
3338 GetExceptRangeForPc(pc, catchOnly, codePtr)
3339 unsigned char *pc; /* The program counter value for which to
3340 * search for a closest enclosing exception
3341 * range. This points to a bytecode
3342 * instruction in codePtr's code. */
3343 int catchOnly; /* If 0, consider either loop or catch
3344 * ExceptionRanges in search. If nonzero
3345 * consider only catch ranges (and ignore
3346 * any closer loop ranges). */
3347 ByteCode* codePtr; /* Points to the ByteCode in which to search
3348 * for the enclosing ExceptionRange. */
3349 {
3350 ExceptionRange *rangeArrayPtr;
3351 int numRanges = codePtr->numExceptRanges;
3352 register ExceptionRange *rangePtr;
3353 int pcOffset = (pc - codePtr->codeStart);
3354 register int i, level;
3355
3356 if (numRanges == 0) {
3357 return NULL;
3358 }
3359 rangeArrayPtr = codePtr->exceptArrayPtr;
3360
3361 for (level = codePtr->maxExceptDepth; level >= 0; level--) {
3362 for (i = 0; i < numRanges; i++) {
3363 rangePtr = &(rangeArrayPtr[i]);
3364 if (rangePtr->nestingLevel == level) {
3365 int start = rangePtr->codeOffset;
3366 int end = (start + rangePtr->numCodeBytes);
3367 if ((start <= pcOffset) && (pcOffset < end)) {
3368 if ((!catchOnly)
3369 || (rangePtr->type == CATCH_EXCEPTION_RANGE)) {
3370 return rangePtr;
3371 }
3372 }
3373 }
3374 }
3375 }
3376 return NULL;
3377 }
3378
3379 /*
3380 *----------------------------------------------------------------------
3381 *
3382 * GetOpcodeName --
3383 *
3384 * This procedure is called by the TRACE and TRACE_WITH_OBJ macros
3385 * used in TclExecuteByteCode when debugging. It returns the name of
3386 * the bytecode instruction at a specified instruction pc.
3387 *
3388 * Results:
3389 * A character string for the instruction.
3390 *
3391 * Side effects:
3392 * None.
3393 *
3394 *----------------------------------------------------------------------
3395 */
3396
3397 #ifdef TCL_COMPILE_DEBUG
3398 static char *
3399 GetOpcodeName(pc)
3400 unsigned char *pc; /* Points to the instruction whose name
3401 * should be returned. */
3402 {
3403 unsigned char opCode = *pc;
3404
3405 return instructionTable[opCode].name;
3406 }
3407 #endif /* TCL_COMPILE_DEBUG */
3408
3409 /*
3410 *----------------------------------------------------------------------
3411 *
3412 * VerifyExprObjType --
3413 *
3414 * This procedure is called by the math functions to verify that
3415 * the object is either an int or double, coercing it if necessary.
3416 * If an error occurs during conversion, an error message is left
3417 * in the interpreter's result unless "interp" is NULL.
3418 *
3419 * Results:
3420 * TCL_OK if it was int or double, TCL_ERROR otherwise
3421 *
3422 * Side effects:
3423 * objPtr is ensured to be either tclIntType of tclDoubleType.
3424 *
3425 *----------------------------------------------------------------------
3426 */
3427
3428 static int
3429 VerifyExprObjType(interp, objPtr)
3430 Tcl_Interp *interp; /* The interpreter in which to execute the
3431 * function. */
3432 Tcl_Obj *objPtr; /* Points to the object to type check. */
3433 {
3434 if ((objPtr->typePtr == &tclIntType) ||
3435 (objPtr->typePtr == &tclDoubleType)) {
3436 return TCL_OK;
3437 } else {
3438 int length, result = TCL_OK;
3439 char *s = Tcl_GetStringFromObj(objPtr, &length);
3440
3441 if (TclLooksLikeInt(s, length)) {
3442 long i;
3443 result = Tcl_GetLongFromObj((Tcl_Interp *) NULL, objPtr, &i);
3444 } else {
3445 double d;
3446 result = Tcl_GetDoubleFromObj((Tcl_Interp *) NULL, objPtr, &d);
3447 }
3448 if ((result != TCL_OK) && (interp != NULL)) {
3449 Tcl_ResetResult(interp);
3450 if (TclCheckBadOctal((Tcl_Interp *) NULL, s)) {
3451 Tcl_AppendToObj(Tcl_GetObjResult(interp),
3452 "argument to math function was an invalid octal number",
3453 -1);
3454 } else {
3455 Tcl_AppendToObj(Tcl_GetObjResult(interp),
3456 "argument to math function didn't have numeric value",
3457 -1);
3458 }
3459 }
3460 return result;
3461 }
3462 }
3463
3464 /*
3465 *----------------------------------------------------------------------
3466 *
3467 * Math Functions --
3468 *
3469 * This page contains the procedures that implement all of the
3470 * built-in math functions for expressions.
3471 *
3472 * Results:
3473 * Each procedure returns TCL_OK if it succeeds and pushes an
3474 * Tcl object holding the result. If it fails it returns TCL_ERROR
3475 * and leaves an error message in the interpreter's result.
3476 *
3477 * Side effects:
3478 * None.
3479 *
3480 *----------------------------------------------------------------------
3481 */
3482
3483 static int
3484 ExprUnaryFunc(interp, eePtr, clientData)
3485 Tcl_Interp *interp; /* The interpreter in which to execute the
3486 * function. */
3487 ExecEnv *eePtr; /* Points to the environment for executing
3488 * the function. */
3489 ClientData clientData; /* Contains the address of a procedure that
3490 * takes one double argument and returns a
3491 * double result. */
3492 {
3493 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3494 register int stackTop; /* Cached top index of evaluation stack. */
3495 register Tcl_Obj *valuePtr;
3496 double d, dResult;
3497 int result;
3498
3499 double (*func) _ANSI_ARGS_((double)) =
3500 (double (*)_ANSI_ARGS_((double))) clientData;
3501
3502 /*
3503 * Set stackPtr and stackTop from eePtr.
3504 */
3505
3506 result = TCL_OK;
3507 CACHE_STACK_INFO();
3508
3509 /*
3510 * Pop the function's argument from the evaluation stack. Convert it
3511 * to a double if necessary.
3512 */
3513
3514 valuePtr = POP_OBJECT();
3515
3516 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
3517 result = TCL_ERROR;
3518 goto done;
3519 }
3520
3521 if (valuePtr->typePtr == &tclIntType) {
3522 d = (double) valuePtr->internalRep.longValue;
3523 } else {
3524 d = valuePtr->internalRep.doubleValue;
3525 }
3526
3527 errno = 0;
3528 dResult = (*func)(d);
3529 if ((errno != 0) || IS_NAN(dResult) || IS_INF(dResult)) {
3530 TclExprFloatError(interp, dResult);
3531 result = TCL_ERROR;
3532 goto done;
3533 }
3534
3535 /*
3536 * Push a Tcl object holding the result.
3537 */
3538
3539 PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
3540
3541 /*
3542 * Reflect the change to stackTop back in eePtr.
3543 */
3544
3545 done:
3546 Tcl_DecrRefCount(valuePtr);
3547 DECACHE_STACK_INFO();
3548 return result;
3549 }
3550
3551 static int
3552 ExprBinaryFunc(interp, eePtr, clientData)
3553 Tcl_Interp *interp; /* The interpreter in which to execute the
3554 * function. */
3555 ExecEnv *eePtr; /* Points to the environment for executing
3556 * the function. */
3557 ClientData clientData; /* Contains the address of a procedure that
3558 * takes two double arguments and
3559 * returns a double result. */
3560 {
3561 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3562 register int stackTop; /* Cached top index of evaluation stack. */
3563 register Tcl_Obj *valuePtr, *value2Ptr;
3564 double d1, d2, dResult;
3565 int result;
3566
3567 double (*func) _ANSI_ARGS_((double, double))
3568 = (double (*)_ANSI_ARGS_((double, double))) clientData;
3569
3570 /*
3571 * Set stackPtr and stackTop from eePtr.
3572 */
3573
3574 result = TCL_OK;
3575 CACHE_STACK_INFO();
3576
3577 /*
3578 * Pop the function's two arguments from the evaluation stack. Convert
3579 * them to doubles if necessary.
3580 */
3581
3582 value2Ptr = POP_OBJECT();
3583 valuePtr = POP_OBJECT();
3584
3585 if ((VerifyExprObjType(interp, valuePtr) != TCL_OK) ||
3586 (VerifyExprObjType(interp, value2Ptr) != TCL_OK)) {
3587 result = TCL_ERROR;
3588 goto done;
3589 }
3590
3591 if (valuePtr->typePtr == &tclIntType) {
3592 d1 = (double) valuePtr->internalRep.longValue;
3593 } else {
3594 d1 = valuePtr->internalRep.doubleValue;
3595 }
3596
3597 if (value2Ptr->typePtr == &tclIntType) {
3598 d2 = (double) value2Ptr->internalRep.longValue;
3599 } else {
3600 d2 = value2Ptr->internalRep.doubleValue;
3601 }
3602
3603 errno = 0;
3604 dResult = (*func)(d1, d2);
3605 if ((errno != 0) || IS_NAN(dResult) || IS_INF(dResult)) {
3606 TclExprFloatError(interp, dResult);
3607 result = TCL_ERROR;
3608 goto done;
3609 }
3610
3611 /*
3612 * Push a Tcl object holding the result.
3613 */
3614
3615 PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
3616
3617 /*
3618 * Reflect the change to stackTop back in eePtr.
3619 */
3620
3621 done:
3622 Tcl_DecrRefCount(valuePtr);
3623 Tcl_DecrRefCount(value2Ptr);
3624 DECACHE_STACK_INFO();
3625 return result;
3626 }
3627
3628 static int
3629 ExprAbsFunc(interp, eePtr, clientData)
3630 Tcl_Interp *interp; /* The interpreter in which to execute the
3631 * function. */
3632 ExecEnv *eePtr; /* Points to the environment for executing
3633 * the function. */
3634 ClientData clientData; /* Ignored. */
3635 {
3636 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3637 register int stackTop; /* Cached top index of evaluation stack. */
3638 register Tcl_Obj *valuePtr;
3639 long i, iResult;
3640 double d, dResult;
3641 int result;
3642
3643 /*
3644 * Set stackPtr and stackTop from eePtr.
3645 */
3646
3647 result = TCL_OK;
3648 CACHE_STACK_INFO();
3649
3650 /*
3651 * Pop the argument from the evaluation stack.
3652 */
3653
3654 valuePtr = POP_OBJECT();
3655
3656 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
3657 result = TCL_ERROR;
3658 goto done;
3659 }
3660
3661 /*
3662 * Push a Tcl object with the result.
3663 */
3664 if (valuePtr->typePtr == &tclIntType) {
3665 i = valuePtr->internalRep.longValue;
3666 if (i < 0) {
3667 iResult = -i;
3668 if (iResult < 0) {
3669 Tcl_ResetResult(interp);
3670 Tcl_AppendToObj(Tcl_GetObjResult(interp),
3671 "integer value too large to represent", -1);
3672 Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
3673 "integer value too large to represent", (char *) NULL);
3674 result = TCL_ERROR;
3675 goto done;
3676 }
3677 } else {
3678 iResult = i;
3679 }
3680 PUSH_OBJECT(Tcl_NewLongObj(iResult));
3681 } else {
3682 d = valuePtr->internalRep.doubleValue;
3683 if (d < 0.0) {
3684 dResult = -d;
3685 } else {
3686 dResult = d;
3687 }
3688 if (IS_NAN(dResult) || IS_INF(dResult)) {
3689 TclExprFloatError(interp, dResult);
3690 result = TCL_ERROR;
3691 goto done;
3692 }
3693 PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
3694 }
3695
3696 /*
3697 * Reflect the change to stackTop back in eePtr.
3698 */
3699
3700 done:
3701 Tcl_DecrRefCount(valuePtr);
3702 DECACHE_STACK_INFO();
3703 return result;
3704 }
3705
3706 static int
3707 ExprDoubleFunc(interp, eePtr, clientData)
3708 Tcl_Interp *interp; /* The interpreter in which to execute the
3709 * function. */
3710 ExecEnv *eePtr; /* Points to the environment for executing
3711 * the function. */
3712 ClientData clientData; /* Ignored. */
3713 {
3714 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3715 register int stackTop; /* Cached top index of evaluation stack. */
3716 register Tcl_Obj *valuePtr;
3717 double dResult;
3718 int result;
3719
3720 /*
3721 * Set stackPtr and stackTop from eePtr.
3722 */
3723
3724 result = TCL_OK;
3725 CACHE_STACK_INFO();
3726
3727 /*
3728 * Pop the argument from the evaluation stack.
3729 */
3730
3731 valuePtr = POP_OBJECT();
3732
3733 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
3734 result = TCL_ERROR;
3735 goto done;
3736 }
3737
3738 if (valuePtr->typePtr == &tclIntType) {
3739 dResult = (double) valuePtr->internalRep.longValue;
3740 } else {
3741 dResult = valuePtr->internalRep.doubleValue;
3742 }
3743
3744 /*
3745 * Push a Tcl object with the result.
3746 */
3747
3748 PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
3749
3750 /*
3751 * Reflect the change to stackTop back in eePtr.
3752 */
3753
3754 done:
3755 Tcl_DecrRefCount(valuePtr);
3756 DECACHE_STACK_INFO();
3757 return result;
3758 }
3759
3760 static int
3761 ExprIntFunc(interp, eePtr, clientData)
3762 Tcl_Interp *interp; /* The interpreter in which to execute the
3763 * function. */
3764 ExecEnv *eePtr; /* Points to the environment for executing
3765 * the function. */
3766 ClientData clientData; /* Ignored. */
3767 {
3768 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3769 register int stackTop; /* Cached top index of evaluation stack. */
3770 register Tcl_Obj *valuePtr;
3771 long iResult;
3772 double d;
3773 int result;
3774
3775 /*
3776 * Set stackPtr and stackTop from eePtr.
3777 */
3778
3779 result = TCL_OK;
3780 CACHE_STACK_INFO();
3781
3782 /*
3783 * Pop the argument from the evaluation stack.
3784 */
3785
3786 valuePtr = POP_OBJECT();
3787
3788 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
3789 result = TCL_ERROR;
3790 goto done;
3791 }
3792
3793 if (valuePtr->typePtr == &tclIntType) {
3794 iResult = valuePtr->internalRep.longValue;
3795 } else {
3796 d = valuePtr->internalRep.doubleValue;
3797 if (d < 0.0) {
3798 if (d < (double) (long) LONG_MIN) {
3799 tooLarge:
3800 Tcl_ResetResult(interp);
3801 Tcl_AppendToObj(Tcl_GetObjResult(interp),
3802 "integer value too large to represent", -1);
3803 Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
3804 "integer value too large to represent", (char *) NULL);
3805 result = TCL_ERROR;
3806 goto done;
3807 }
3808 } else {
3809 if (d > (double) LONG_MAX) {
3810 goto tooLarge;
3811 }
3812 }
3813 if (IS_NAN(d) || IS_INF(d)) {
3814 TclExprFloatError(interp, d);
3815 result = TCL_ERROR;
3816 goto done;
3817 }
3818 iResult = (long) d;
3819 }
3820
3821 /*
3822 * Push a Tcl object with the result.
3823 */
3824
3825 PUSH_OBJECT(Tcl_NewLongObj(iResult));
3826
3827 /*
3828 * Reflect the change to stackTop back in eePtr.
3829 */
3830
3831 done:
3832 Tcl_DecrRefCount(valuePtr);
3833 DECACHE_STACK_INFO();
3834 return result;
3835 }
3836
3837 static int
3838 ExprRandFunc(interp, eePtr, clientData)
3839 Tcl_Interp *interp; /* The interpreter in which to execute the
3840 * function. */
3841 ExecEnv *eePtr; /* Points to the environment for executing
3842 * the function. */
3843 ClientData clientData; /* Ignored. */
3844 {
3845 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3846 register int stackTop; /* Cached top index of evaluation stack. */
3847 Interp *iPtr = (Interp *) interp;
3848 double dResult;
3849 int tmp;
3850
3851 if (!(iPtr->flags & RAND_SEED_INITIALIZED)) {
3852 iPtr->flags |= RAND_SEED_INITIALIZED;
3853 iPtr->randSeed = TclpGetClicks();
3854 }
3855
3856 /*
3857 * Set stackPtr and stackTop from eePtr.
3858 */
3859
3860 CACHE_STACK_INFO();
3861
3862 /*
3863 * Generate the random number using the linear congruential
3864 * generator defined by the following recurrence:
3865 * seed = ( IA * seed ) mod IM
3866 * where IA is 16807 and IM is (2^31) - 1. In order to avoid
3867 * potential problems with integer overflow, the code uses
3868 * additional constants IQ and IR such that
3869 * IM = IA*IQ + IR
3870 * For details on how this algorithm works, refer to the following
3871 * papers:
3872 *
3873 * S.K. Park & K.W. Miller, "Random number generators: good ones
3874 * are hard to find," Comm ACM 31(10):1192-1201, Oct 1988
3875 *
3876 * W.H. Press & S.A. Teukolsky, "Portable random number
3877 * generators," Computers in Physics 6(5):522-524, Sep/Oct 1992.
3878 */
3879
3880 #define RAND_IA 16807
3881 #define RAND_IM 2147483647
3882 #define RAND_IQ 127773
3883 #define RAND_IR 2836
3884 #define RAND_MASK 123459876
3885
3886 if (iPtr->randSeed == 0) {
3887 /*
3888 * Don't allow a 0 seed, since it breaks the generator. Shift
3889 * it to some other value.
3890 */
3891
3892 iPtr->randSeed = 123459876;
3893 }
3894 tmp = iPtr->randSeed/RAND_IQ;
3895 iPtr->randSeed = RAND_IA*(iPtr->randSeed - tmp*RAND_IQ) - RAND_IR*tmp;
3896 if (iPtr->randSeed < 0) {
3897 iPtr->randSeed += RAND_IM;
3898 }
3899
3900 /*
3901 * On 64-bit architectures we need to mask off the upper bits to
3902 * ensure we only have a 32-bit range. The constant has the
3903 * bizarre form below in order to make sure that it doesn't
3904 * get sign-extended (the rules for sign extension are very
3905 * concat, particularly on 64-bit machines).
3906 */
3907
3908 iPtr->randSeed &= ((((unsigned long) 0xfffffff) << 4) | 0xf);
3909 dResult = iPtr->randSeed * (1.0/RAND_IM);
3910
3911 /*
3912 * Push a Tcl object with the result.
3913 */
3914
3915 PUSH_OBJECT(Tcl_NewDoubleObj(dResult));
3916
3917 /*
3918 * Reflect the change to stackTop back in eePtr.
3919 */
3920
3921 DECACHE_STACK_INFO();
3922 return TCL_OK;
3923 }
3924
3925 static int
3926 ExprRoundFunc(interp, eePtr, clientData)
3927 Tcl_Interp *interp; /* The interpreter in which to execute the
3928 * function. */
3929 ExecEnv *eePtr; /* Points to the environment for executing
3930 * the function. */
3931 ClientData clientData; /* Ignored. */
3932 {
3933 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
3934 register int stackTop; /* Cached top index of evaluation stack. */
3935 Tcl_Obj *valuePtr;
3936 long iResult;
3937 double d, temp;
3938 int result;
3939
3940 /*
3941 * Set stackPtr and stackTop from eePtr.
3942 */
3943
3944 result = TCL_OK;
3945 CACHE_STACK_INFO();
3946
3947 /*
3948 * Pop the argument from the evaluation stack.
3949 */
3950
3951 valuePtr = POP_OBJECT();
3952
3953 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
3954 result = TCL_ERROR;
3955 goto done;
3956 }
3957
3958 if (valuePtr->typePtr == &tclIntType) {
3959 iResult = valuePtr->internalRep.longValue;
3960 } else {
3961 d = valuePtr->internalRep.doubleValue;
3962 if (d < 0.0) {
3963 if (d <= (((double) (long) LONG_MIN) - 0.5)) {
3964 tooLarge:
3965 Tcl_ResetResult(interp);
3966 Tcl_AppendToObj(Tcl_GetObjResult(interp),
3967 "integer value too large to represent", -1);
3968 Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW",
3969 "integer value too large to represent",
3970 (char *) NULL);
3971 result = TCL_ERROR;
3972 goto done;
3973 }
3974 temp = (long) (d - 0.5);
3975 } else {
3976 if (d >= (((double) LONG_MAX + 0.5))) {
3977 goto tooLarge;
3978 }
3979 temp = (long) (d + 0.5);
3980 }
3981 if (IS_NAN(temp) || IS_INF(temp)) {
3982 TclExprFloatError(interp, temp);
3983 result = TCL_ERROR;
3984 goto done;
3985 }
3986 iResult = (long) temp;
3987 }
3988
3989 /*
3990 * Push a Tcl object with the result.
3991 */
3992
3993 PUSH_OBJECT(Tcl_NewLongObj(iResult));
3994
3995 /*
3996 * Reflect the change to stackTop back in eePtr.
3997 */
3998
3999 done:
4000 Tcl_DecrRefCount(valuePtr);
4001 DECACHE_STACK_INFO();
4002 return result;
4003 }
4004
4005 static int
4006 ExprSrandFunc(interp, eePtr, clientData)
4007 Tcl_Interp *interp; /* The interpreter in which to execute the
4008 * function. */
4009 ExecEnv *eePtr; /* Points to the environment for executing
4010 * the function. */
4011 ClientData clientData; /* Ignored. */
4012 {
4013 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
4014 register int stackTop; /* Cached top index of evaluation stack. */
4015 Interp *iPtr = (Interp *) interp;
4016 Tcl_Obj *valuePtr;
4017 long i = 0; /* Initialized to avoid compiler warning. */
4018 int result;
4019
4020 /*
4021 * Set stackPtr and stackTop from eePtr.
4022 */
4023
4024 CACHE_STACK_INFO();
4025
4026 /*
4027 * Pop the argument from the evaluation stack. Use the value
4028 * to reset the random number seed.
4029 */
4030
4031 valuePtr = POP_OBJECT();
4032
4033 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
4034 result = TCL_ERROR;
4035 goto badValue;
4036 }
4037
4038 if (valuePtr->typePtr == &tclIntType) {
4039 i = valuePtr->internalRep.longValue;
4040 } else {
4041 /*
4042 * At this point, the only other possible type is double
4043 */
4044 Tcl_ResetResult(interp);
4045 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
4046 "can't use floating-point value as argument to srand",
4047 (char *) NULL);
4048 badValue:
4049 Tcl_DecrRefCount(valuePtr);
4050 DECACHE_STACK_INFO();
4051 return TCL_ERROR;
4052 }
4053
4054 /*
4055 * Reset the seed.
4056 */
4057
4058 iPtr->flags |= RAND_SEED_INITIALIZED;
4059 iPtr->randSeed = i;
4060
4061 /*
4062 * To avoid duplicating the random number generation code we simply
4063 * clean up our state and call the real random number function. That
4064 * function will always succeed.
4065 */
4066
4067 Tcl_DecrRefCount(valuePtr);
4068 DECACHE_STACK_INFO();
4069
4070 ExprRandFunc(interp, eePtr, clientData);
4071 return TCL_OK;
4072 }
4073
4074 /*
4075 *----------------------------------------------------------------------
4076 *
4077 * ExprCallMathFunc --
4078 *
4079 * This procedure is invoked to call a non-builtin math function
4080 * during the execution of an expression.
4081 *
4082 * Results:
4083 * TCL_OK is returned if all went well and the function's value
4084 * was computed successfully. If an error occurred, TCL_ERROR
4085 * is returned and an error message is left in the interpreter's
4086 * result. After a successful return this procedure pushes a Tcl object
4087 * holding the result.
4088 *
4089 * Side effects:
4090 * None, unless the called math function has side effects.
4091 *
4092 *----------------------------------------------------------------------
4093 */
4094
4095 static int
4096 ExprCallMathFunc(interp, eePtr, objc, objv)
4097 Tcl_Interp *interp; /* The interpreter in which to execute the
4098 * function. */
4099 ExecEnv *eePtr; /* Points to the environment for executing
4100 * the function. */
4101 int objc; /* Number of arguments. The function name is
4102 * the 0-th argument. */
4103 Tcl_Obj **objv; /* The array of arguments. The function name
4104 * is objv[0]. */
4105 {
4106 Interp *iPtr = (Interp *) interp;
4107 Tcl_Obj **stackPtr; /* Cached evaluation stack base pointer. */
4108 register int stackTop; /* Cached top index of evaluation stack. */
4109 char *funcName;
4110 Tcl_HashEntry *hPtr;
4111 MathFunc *mathFuncPtr; /* Information about math function. */
4112 Tcl_Value args[MAX_MATH_ARGS]; /* Arguments for function call. */
4113 Tcl_Value funcResult; /* Result of function call as Tcl_Value. */
4114 register Tcl_Obj *valuePtr;
4115 long i;
4116 double d;
4117 int j, k, result;
4118 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
4119
4120 Tcl_ResetResult(interp);
4121
4122 /*
4123 * Set stackPtr and stackTop from eePtr.
4124 */
4125
4126 CACHE_STACK_INFO();
4127
4128 /*
4129 * Look up the MathFunc record for the function.
4130 */
4131
4132 funcName = Tcl_GetString(objv[0]);
4133 hPtr = Tcl_FindHashEntry(&iPtr->mathFuncTable, funcName);
4134 if (hPtr == NULL) {
4135 Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
4136 "unknown math function \"", funcName, "\"", (char *) NULL);
4137 result = TCL_ERROR;
4138 goto done;
4139 }
4140 mathFuncPtr = (MathFunc *) Tcl_GetHashValue(hPtr);
4141 if (mathFuncPtr->numArgs != (objc-1)) {
4142 panic("ExprCallMathFunc: expected number of args %d != actual number %d",
4143 mathFuncPtr->numArgs, objc);
4144 result = TCL_ERROR;
4145 goto done;
4146 }
4147
4148 /*
4149 * Collect the arguments for the function, if there are any, into the
4150 * array "args". Note that args[0] will have the Tcl_Value that
4151 * corresponds to objv[1].
4152 */
4153
4154 for (j = 1, k = 0; j < objc; j++, k++) {
4155 valuePtr = objv[j];
4156
4157 if (VerifyExprObjType(interp, valuePtr) != TCL_OK) {
4158 result = TCL_ERROR;
4159 goto done;
4160 }
4161
4162 /*
4163 * Copy the object's numeric value to the argument record,
4164 * converting it if necessary.
4165 */
4166
4167 if (valuePtr->typePtr == &tclIntType) {
4168 i = valuePtr->internalRep.longValue;
4169 if (mathFuncPtr->argTypes[k] == TCL_DOUBLE) {
4170 args[k].type = TCL_DOUBLE;
4171 args[k].doubleValue = i;
4172 } else {
4173 args[k].type = TCL_INT;
4174 args[k].intValue = i;
4175 }
4176 } else {
4177 d = valuePtr->internalRep.doubleValue;
4178 if (mathFuncPtr->argTypes[k] == TCL_INT) {
4179 args[k].type = TCL_INT;
4180 args[k].intValue = (long) d;
4181 } else {
4182 args[k].type = TCL_DOUBLE;
4183 args[k].doubleValue = d;
4184 }
4185 }
4186 }
4187
4188 /*
4189 * Invoke the function and copy its result back into valuePtr.
4190 */
4191
4192 tsdPtr->mathInProgress++;
4193 result = (*mathFuncPtr->proc)(mathFuncPtr->clientData, interp, args,
4194 &funcResult);
4195 tsdPtr->mathInProgress--;
4196 if (result != TCL_OK) {
4197 goto done;
4198 }
4199
4200 /*
4201 * Pop the objc top stack elements and decrement their ref counts.
4202 */
4203
4204 i = (stackTop - (objc-1));
4205 while (i <= stackTop) {
4206 valuePtr = stackPtr[i];
4207 Tcl_DecrRefCount(valuePtr);
4208 i++;
4209 }
4210 stackTop -= objc;
4211
4212 /*
4213 * Push the call's object result.
4214 */
4215
4216 if (funcResult.type == TCL_INT) {
4217 PUSH_OBJECT(Tcl_NewLongObj(funcResult.intValue));
4218 } else {
4219 d = funcResult.doubleValue;
4220 if (IS_NAN(d) || IS_INF(d)) {
4221 TclExprFloatError(interp, d);
4222 result = TCL_ERROR;
4223 goto done;
4224 }
4225 PUSH_OBJECT(Tcl_NewDoubleObj(d));
4226 }
4227
4228 /*
4229 * Reflect the change to stackTop back in eePtr.
4230 */
4231
4232 done:
4233 DECACHE_STACK_INFO();
4234 return result;
4235 }
4236
4237 /*
4238 *----------------------------------------------------------------------
4239 *
4240 * TclExprFloatError --
4241 *
4242 * This procedure is called when an error occurs during a
4243 * floating-point operation. It reads errno and sets
4244 * interp->objResultPtr accordingly.
4245 *
4246 * Results:
4247 * interp->objResultPtr is set to hold an error message.
4248 *
4249 * Side effects:
4250 * None.
4251 *
4252 *----------------------------------------------------------------------
4253 */
4254
4255 void
4256 TclExprFloatError(interp, value)
4257 Tcl_Interp *interp; /* Where to store error message. */
4258 double value; /* Value returned after error; used to
4259 * distinguish underflows from overflows. */
4260 {
4261 char *s;
4262
4263 Tcl_ResetResult(interp);
4264 if ((errno == EDOM) || (value != value)) {
4265 s = "domain error: argument not in valid range";
4266 Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
4267 Tcl_SetErrorCode(interp, "ARITH", "DOMAIN", s, (char *) NULL);
4268 } else if ((errno == ERANGE) || IS_INF(value)) {
4269 if (value == 0.0) {
4270 s = "floating-point value too small to represent";
4271 Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
4272 Tcl_SetErrorCode(interp, "ARITH", "UNDERFLOW", s, (char *) NULL);
4273 } else {
4274 s = "floating-point value too large to represent";
4275 Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
4276 Tcl_SetErrorCode(interp, "ARITH", "OVERFLOW", s, (char *) NULL);
4277 }
4278 } else {
4279 char msg[64 + TCL_INTEGER_SPACE];
4280
4281 sprintf(msg, "unknown floating-point error, errno = %d", errno);
4282 Tcl_AppendToObj(Tcl_GetObjResult(interp), msg, -1);
4283 Tcl_SetErrorCode(interp, "ARITH", "UNKNOWN", msg, (char *) NULL);
4284 }
4285 }
4286
4287 /*
4288 *----------------------------------------------------------------------
4289 *
4290 * TclMathInProgress --
4291 *
4292 * This procedure is called to find out if Tcl is doing math
4293 * in this thread.
4294 *
4295 * Results:
4296 * 0 or 1.
4297 *
4298 * Side effects:
4299 * None.
4300 *
4301 *----------------------------------------------------------------------
4302 */
4303
4304 int
4305 TclMathInProgress()
4306 {
4307 ThreadSpecificData *tsdPtr = TCL_TSD_INIT(&dataKey);
4308 return tsdPtr->mathInProgress;
4309 }
4310
4311 #ifdef TCL_COMPILE_STATS
4312 /*
4313 *----------------------------------------------------------------------
4314 *
4315 * TclLog2 --
4316 *
4317 * Procedure used while collecting compilation statistics to determine
4318 * the log base 2 of an integer.
4319 *
4320 * Results:
4321 * Returns the log base 2 of the operand. If the argument is less
4322 * than or equal to zero, a zero is returned.
4323 *
4324 * Side effects:
4325 * None.
4326 *
4327 *----------------------------------------------------------------------
4328 */
4329
4330 int
4331 TclLog2(value)
4332 register int value; /* The integer for which to compute the
4333 * log base 2. */
4334 {
4335 register int n = value;
4336 register int result = 0;
4337
4338 while (n > 1) {
4339 n = n >> 1;
4340 result++;
4341 }
4342 return result;
4343 }
4344
4345 /*
4346 *----------------------------------------------------------------------
4347 *
4348 * EvalStatsCmd --
4349 *
4350 * Implements the "evalstats" command that prints instruction execution
4351 * counts to stdout.
4352 *
4353 * Results:
4354 * Standard Tcl results.
4355 *
4356 * Side effects:
4357 * None.
4358 *
4359 *----------------------------------------------------------------------
4360 */
4361
4362 static int
4363 EvalStatsCmd(unused, interp, argc, argv)
4364 ClientData unused; /* Unused. */
4365 Tcl_Interp *interp; /* The current interpreter. */
4366 int argc; /* The number of arguments. */
4367 char **argv; /* The argument strings. */
4368 {
4369 Interp *iPtr = (Interp *) interp;
4370 LiteralTable *globalTablePtr = &(iPtr->literalTable);
4371 ByteCodeStats *statsPtr = &(iPtr->stats);
4372 double totalCodeBytes, currentCodeBytes;
4373 double totalLiteralBytes, currentLiteralBytes;
4374 double objBytesIfUnshared, strBytesIfUnshared, sharingBytesSaved;
4375 double strBytesSharedMultX, strBytesSharedOnce;
4376 double numInstructions, currentHeaderBytes;
4377 long numCurrentByteCodes, numByteCodeLits;
4378 long refCountSum, literalMgmtBytes, sum;
4379 int numSharedMultX, numSharedOnce;
4380 int decadeHigh, minSizeDecade, maxSizeDecade, length, i;
4381 char *litTableStats;
4382 LiteralEntry *entryPtr;
4383
4384 numInstructions = 0.0;
4385 for (i = 0; i < 256; i++) {
4386 if (statsPtr->instructionCount[i] != 0) {
4387 numInstructions += statsPtr->instructionCount[i];
4388 }
4389 }
4390
4391 totalLiteralBytes = sizeof(LiteralTable)
4392 + iPtr->literalTable.numBuckets * sizeof(LiteralEntry *)
4393 + (statsPtr->numLiteralsCreated * sizeof(LiteralEntry))
4394 + (statsPtr->numLiteralsCreated * sizeof(Tcl_Obj))
4395 + statsPtr->totalLitStringBytes;
4396 totalCodeBytes = statsPtr->totalByteCodeBytes + totalLiteralBytes;
4397
4398 numCurrentByteCodes =
4399 statsPtr->numCompilations - statsPtr->numByteCodesFreed;
4400 currentHeaderBytes = numCurrentByteCodes
4401 * (sizeof(ByteCode) - (sizeof(size_t) + sizeof(Tcl_Time)));
4402 literalMgmtBytes = sizeof(LiteralTable)
4403 + (iPtr->literalTable.numBuckets * sizeof(LiteralEntry *))
4404 + (iPtr->literalTable.numEntries * sizeof(LiteralEntry));
4405 currentLiteralBytes = literalMgmtBytes
4406 + iPtr->literalTable.numEntries * sizeof(Tcl_Obj)
4407 + statsPtr->currentLitStringBytes;
4408 currentCodeBytes = statsPtr->currentByteCodeBytes + currentLiteralBytes;
4409
4410 /*
4411 * Summary statistics, total and current source and ByteCode sizes.
4412 */
4413
4414 fprintf(stdout, "\n----------------------------------------------------------------\n");
4415 fprintf(stdout,
4416 "Compilation and execution statistics for interpreter 0x%x\n",
4417 (unsigned int) iPtr);
4418
4419 fprintf(stdout, "\nNumber ByteCodes executed %ld\n",
4420 statsPtr->numExecutions);
4421 fprintf(stdout, "Number ByteCodes compiled %ld\n",
4422 statsPtr->numCompilations);
4423 fprintf(stdout, " Mean executions/compile %.1f\n",
4424 ((float)statsPtr->numExecutions) / ((float)statsPtr->numCompilations));
4425
4426 fprintf(stdout, "\nInstructions executed %.0f\n",
4427 numInstructions);
4428 fprintf(stdout, " Mean inst/compile %.0f\n",
4429 numInstructions / statsPtr->numCompilations);
4430 fprintf(stdout, " Mean inst/execution %.0f\n",
4431 numInstructions / statsPtr->numExecutions);
4432
4433 fprintf(stdout, "\nTotal ByteCodes %ld\n",
4434 statsPtr->numCompilations);
4435 fprintf(stdout, " Source bytes %.6g\n",
4436 statsPtr->totalSrcBytes);
4437 fprintf(stdout, " Code bytes %.6g\n",
4438 totalCodeBytes);
4439 fprintf(stdout, " ByteCode bytes %.6g\n",
4440 statsPtr->totalByteCodeBytes);
4441 fprintf(stdout, " Literal bytes %.6g\n",
4442 totalLiteralBytes);
4443 fprintf(stdout, " table %d + bkts %d + entries %ld + objects %ld + strings %.6g\n",
4444 sizeof(LiteralTable),
4445 iPtr->literalTable.numBuckets * sizeof(LiteralEntry *),
4446 statsPtr->numLiteralsCreated * sizeof(LiteralEntry),
4447 statsPtr->numLiteralsCreated * sizeof(Tcl_Obj),
4448 statsPtr->totalLitStringBytes);
4449 fprintf(stdout, " Mean code/compile %.1f\n",
4450 totalCodeBytes / statsPtr->numCompilations);
4451 fprintf(stdout, " Mean code/source %.1f\n",
4452 totalCodeBytes / statsPtr->totalSrcBytes);
4453
4454 fprintf(stdout, "\nCurrent ByteCodes %ld\n",
4455 numCurrentByteCodes);
4456 fprintf(stdout, " Source bytes %.6g\n",
4457 statsPtr->currentSrcBytes);
4458 fprintf(stdout, " Code bytes %.6g\n",
4459 currentCodeBytes);
4460 fprintf(stdout, " ByteCode bytes %.6g\n",
4461 statsPtr->currentByteCodeBytes);
4462 fprintf(stdout, " Literal bytes %.6g\n",
4463 currentLiteralBytes);
4464 fprintf(stdout, " table %d + bkts %d + entries %d + objects %d + strings %.6g\n",
4465 sizeof(LiteralTable),
4466 iPtr->literalTable.numBuckets * sizeof(LiteralEntry *),
4467 iPtr->literalTable.numEntries * sizeof(LiteralEntry),
4468 iPtr->literalTable.numEntries * sizeof(Tcl_Obj),
4469 statsPtr->currentLitStringBytes);
4470 fprintf(stdout, " Mean code/source %.1f\n",
4471 currentCodeBytes / statsPtr->currentSrcBytes);
4472 fprintf(stdout, " Code + source bytes %.6g (%0.1f mean code/src)\n",
4473 (currentCodeBytes + statsPtr->currentSrcBytes),
4474 (currentCodeBytes / statsPtr->currentSrcBytes) + 1.0);
4475
4476 /*
4477 * Literal table statistics.
4478 */
4479
4480 numByteCodeLits = 0;
4481 refCountSum = 0;
4482 numSharedMultX = 0;
4483 numSharedOnce = 0;
4484 objBytesIfUnshared = 0.0;
4485 strBytesIfUnshared = 0.0;
4486 strBytesSharedMultX = 0.0;
4487 strBytesSharedOnce = 0.0;
4488 for (i = 0; i < globalTablePtr->numBuckets; i++) {
4489 for (entryPtr = globalTablePtr->buckets[i]; entryPtr != NULL;
4490 entryPtr = entryPtr->nextPtr) {
4491 if (entryPtr->objPtr->typePtr == &tclByteCodeType) {
4492 numByteCodeLits++;
4493 }
4494 (void) Tcl_GetStringFromObj(entryPtr->objPtr, &length);
4495 refCountSum += entryPtr->refCount;
4496 objBytesIfUnshared += (entryPtr->refCount * sizeof(Tcl_Obj));
4497 strBytesIfUnshared += (entryPtr->refCount * (length+1));
4498 if (entryPtr->refCount > 1) {
4499 numSharedMultX++;
4500 strBytesSharedMultX += (length+1);
4501 } else {
4502 numSharedOnce++;
4503 strBytesSharedOnce += (length+1);
4504 }
4505 }
4506 }
4507 sharingBytesSaved = (objBytesIfUnshared + strBytesIfUnshared)
4508 - currentLiteralBytes;
4509
4510 fprintf(stdout, "\nTotal objects (all interps) %ld\n",
4511 tclObjsAlloced);
4512 fprintf(stdout, "Current objects %ld\n",
4513 (tclObjsAlloced - tclObjsFreed));
4514 fprintf(stdout, "Total literal objects %ld\n",
4515 statsPtr->numLiteralsCreated);
4516
4517 fprintf(stdout, "\nCurrent literal objects %d (%0.1f%% of current objects)\n",
4518 globalTablePtr->numEntries,
4519 (globalTablePtr->numEntries * 100.0) / (tclObjsAlloced-tclObjsFreed));
4520 fprintf(stdout, " ByteCode literals %ld (%0.1f%% of current literals)\n",
4521 numByteCodeLits,
4522 (numByteCodeLits * 100.0) / globalTablePtr->numEntries);
4523 fprintf(stdout, " Literals reused > 1x %d\n",
4524 numSharedMultX);
4525 fprintf(stdout, " Mean reference count %.2f\n",
4526 ((double) refCountSum) / globalTablePtr->numEntries);
4527 fprintf(stdout, " Mean len, str reused >1x %.2f\n",
4528 (numSharedMultX? (strBytesSharedMultX/numSharedMultX) : 0.0));
4529 fprintf(stdout, " Mean len, str used 1x %.2f\n",
4530 (numSharedOnce? (strBytesSharedOnce/numSharedOnce) : 0.0));
4531 fprintf(stdout, " Total sharing savings %.6g (%0.1f%% of bytes if no sharing)\n",
4532 sharingBytesSaved,
4533 (sharingBytesSaved * 100.0) / (objBytesIfUnshared + strBytesIfUnshared));
4534 fprintf(stdout, " Bytes with sharing %.6g\n",
4535 currentLiteralBytes);
4536 fprintf(stdout, " table %d + bkts %d + entries %d + objects %d + strings %.6g\n",
4537 sizeof(LiteralTable),
4538 iPtr->literalTable.numBuckets * sizeof(LiteralEntry *),
4539 iPtr->literalTable.numEntries * sizeof(LiteralEntry),
4540 iPtr->literalTable.numEntries * sizeof(Tcl_Obj),
4541 statsPtr->currentLitStringBytes);
4542 fprintf(stdout, " Bytes if no sharing %.6g = objects %.6g + strings %.6g\n",
4543 (objBytesIfUnshared + strBytesIfUnshared),
4544 objBytesIfUnshared, strBytesIfUnshared);
4545 fprintf(stdout, " String sharing savings %.6g = unshared %.6g - shared %.6g\n",
4546 (strBytesIfUnshared - statsPtr->currentLitStringBytes),
4547 strBytesIfUnshared, statsPtr->currentLitStringBytes);
4548 fprintf(stdout, " Literal mgmt overhead %ld (%0.1f%% of bytes with sharing)\n",
4549 literalMgmtBytes,
4550 (literalMgmtBytes * 100.0) / currentLiteralBytes);
4551 fprintf(stdout, " table %d + buckets %d + entries %d\n",
4552 sizeof(LiteralTable),
4553 iPtr->literalTable.numBuckets * sizeof(LiteralEntry *),
4554 iPtr->literalTable.numEntries * sizeof(LiteralEntry));
4555
4556 /*
4557 * Breakdown of current ByteCode space requirements.
4558 */
4559
4560 fprintf(stdout, "\nBreakdown of current ByteCode requirements:\n");
4561 fprintf(stdout, " Bytes Pct of Avg per\n");
4562 fprintf(stdout, " total ByteCode\n");
4563 fprintf(stdout, "Total %12.6g 100.00%% %8.1f\n",
4564 statsPtr->currentByteCodeBytes,
4565 statsPtr->currentByteCodeBytes / numCurrentByteCodes);
4566 fprintf(stdout, "Header %12.6g %8.1f%% %8.1f\n",
4567 currentHeaderBytes,
4568 ((currentHeaderBytes * 100.0) / statsPtr->currentByteCodeBytes),
4569 currentHeaderBytes / numCurrentByteCodes);
4570 fprintf(stdout, "Instructions %12.6g %8.1f%% %8.1f\n",
4571 statsPtr->currentInstBytes,
4572 ((statsPtr->currentInstBytes * 100.0) / statsPtr->currentByteCodeBytes),
4573 statsPtr->currentInstBytes / numCurrentByteCodes);
4574 fprintf(stdout, "Literal ptr array %12.6g %8.1f%% %8.1f\n",
4575 statsPtr->currentLitBytes,
4576 ((statsPtr->currentLitBytes * 100.0) / statsPtr->currentByteCodeBytes),
4577 statsPtr->currentLitBytes / numCurrentByteCodes);
4578 fprintf(stdout, "Exception table %12.6g %8.1f%% %8.1f\n",
4579 statsPtr->currentExceptBytes,
4580 ((statsPtr->currentExceptBytes * 100.0) / statsPtr->currentByteCodeBytes),
4581 statsPtr->currentExceptBytes / numCurrentByteCodes);
4582 fprintf(stdout, "Auxiliary data %12.6g %8.1f%% %8.1f\n",
4583 statsPtr->currentAuxBytes,
4584 ((statsPtr->currentAuxBytes * 100.0) / statsPtr->currentByteCodeBytes),
4585 statsPtr->currentAuxBytes / numCurrentByteCodes);
4586 fprintf(stdout, "Command map %12.6g %8.1f%% %8.1f\n",
4587 statsPtr->currentCmdMapBytes,
4588 ((statsPtr->currentCmdMapBytes * 100.0) / statsPtr->currentByteCodeBytes),
4589 statsPtr->currentCmdMapBytes / numCurrentByteCodes);
4590
4591 /*
4592 * Detailed literal statistics.
4593 */
4594
4595 fprintf(stdout, "\nLiteral string sizes:\n");
4596 fprintf(stdout, " Up to length Percentage\n");
4597 maxSizeDecade = 0;
4598 for (i = 31; i >= 0; i--) {
4599 if (statsPtr->literalCount[i] > 0) {
4600 maxSizeDecade = i;
4601 break;
4602 }
4603 }
4604 sum = 0;
4605 for (i = 0; i <= maxSizeDecade; i++) {
4606 decadeHigh = (1 << (i+1)) - 1;
4607 sum += statsPtr->literalCount[i];
4608 fprintf(stdout, " %10d %8.0f%%\n",
4609 decadeHigh, (sum * 100.0) / statsPtr->numLiteralsCreated);
4610 }
4611
4612 litTableStats = TclLiteralStats(globalTablePtr);
4613 fprintf(stdout, "\nCurrent literal table statistics:\n%s\n",
4614 litTableStats);
4615 ckfree((char *) litTableStats);
4616
4617 /*
4618 * Source and ByteCode size distributions.
4619 */
4620
4621 fprintf(stdout, "\nSource sizes:\n");
4622 fprintf(stdout, " Up to size Percentage\n");
4623 minSizeDecade = maxSizeDecade = 0;
4624 for (i = 0; i < 31; i++) {
4625 if (statsPtr->srcCount[i] > 0) {
4626 minSizeDecade = i;
4627 break;
4628 }
4629 }
4630 for (i = 31; i >= 0; i--) {
4631 if (statsPtr->srcCount[i] > 0) {
4632 maxSizeDecade = i;
4633 break;
4634 }
4635 }
4636 sum = 0;
4637 for (i = minSizeDecade; i <= maxSizeDecade; i++) {
4638 decadeHigh = (1 << (i+1)) - 1;
4639 sum += statsPtr->srcCount[i];
4640 fprintf(stdout, " %10d %8.0f%%\n",
4641 decadeHigh, (sum * 100.0) / statsPtr->numCompilations);
4642 }
4643
4644 fprintf(stdout, "\nByteCode sizes:\n");
4645 fprintf(stdout, " Up to size Percentage\n");
4646 minSizeDecade = maxSizeDecade = 0;
4647 for (i = 0; i < 31; i++) {
4648 if (statsPtr->byteCodeCount[i] > 0) {
4649 minSizeDecade = i;
4650 break;
4651 }
4652 }
4653 for (i = 31; i >= 0; i--) {
4654 if (statsPtr->byteCodeCount[i] > 0) {
4655 maxSizeDecade = i;
4656 break;
4657 }
4658 }
4659 sum = 0;
4660 for (i = minSizeDecade; i <= maxSizeDecade; i++) {
4661 decadeHigh = (1 << (i+1)) - 1;
4662 sum += statsPtr->byteCodeCount[i];
4663 fprintf(stdout, " %10d %8.0f%%\n",
4664 decadeHigh, (sum * 100.0) / statsPtr->numCompilations);
4665 }
4666
4667 fprintf(stdout, "\nByteCode longevity (excludes current ByteCodes):\n");
4668 fprintf(stdout, " Up to ms Percentage\n");
4669 minSizeDecade = maxSizeDecade = 0;
4670 for (i = 0; i < 31; i++) {
4671 if (statsPtr->lifetimeCount[i] > 0) {
4672 minSizeDecade = i;
4673 break;
4674 }
4675 }
4676 for (i = 31; i >= 0; i--) {
4677 if (statsPtr->lifetimeCount[i] > 0) {
4678 maxSizeDecade = i;
4679 break;
4680 }
4681 }
4682 sum = 0;
4683 for (i = minSizeDecade; i <= maxSizeDecade; i++) {
4684 decadeHigh = (1 << (i+1)) - 1;
4685 sum += statsPtr->lifetimeCount[i];
4686 fprintf(stdout, " %12.3f %8.0f%%\n",
4687 decadeHigh / 1000.0,
4688 (sum * 100.0) / statsPtr->numByteCodesFreed);
4689 }
4690
4691 /*
4692 * Instruction counts.
4693 */
4694
4695 fprintf(stdout, "\nInstruction counts:\n");
4696 for (i = 0; i <= LAST_INST_OPCODE; i++) {
4697 if (statsPtr->instructionCount[i]) {
4698 fprintf(stdout, "%20s %8ld %6.1f%%\n",
4699 instructionTable[i].name,
4700 statsPtr->instructionCount[i],
4701 (statsPtr->instructionCount[i]*100.0) / numInstructions);
4702 }
4703 }
4704
4705 fprintf(stdout, "\nInstructions NEVER executed:\n");
4706 for (i = 0; i <= LAST_INST_OPCODE; i++) {
4707 if (statsPtr->instructionCount[i] == 0) {
4708 fprintf(stdout, "%20s\n",
4709 instructionTable[i].name);
4710 }
4711 }
4712
4713 #ifdef TCL_MEM_DEBUG
4714 fprintf(stdout, "\nHeap Statistics:\n");
4715 TclDumpMemoryInfo(stdout);
4716 #endif
4717 fprintf(stdout, "\n----------------------------------------------------------------\n");
4718 return TCL_OK;
4719 }
4720 #endif /* TCL_COMPILE_STATS */
4721
4722 /*
4723 *----------------------------------------------------------------------
4724 *
4725 * Tcl_GetCommandFromObj --
4726 *
4727 * Returns the command specified by the name in a Tcl_Obj.
4728 *
4729 * Results:
4730 * Returns a token for the command if it is found. Otherwise, if it
4731 * can't be found or there is an error, returns NULL.
4732 *
4733 * Side effects:
4734 * May update the internal representation for the object, caching
4735 * the command reference so that the next time this procedure is
4736 * called with the same object, the command can be found quickly.
4737 *
4738 *----------------------------------------------------------------------
4739 */
4740
4741 Tcl_Command
4742 Tcl_GetCommandFromObj(interp, objPtr)
4743 Tcl_Interp *interp; /* The interpreter in which to resolve the
4744 * command and to report errors. */
4745 register Tcl_Obj *objPtr; /* The object containing the command's
4746 * name. If the name starts with "::", will
4747 * be looked up in global namespace. Else,
4748 * looked up first in the current namespace
4749 * if contextNsPtr is NULL, then in global
4750 * namespace. */
4751 {
4752 Interp *iPtr = (Interp *) interp;
4753 register ResolvedCmdName *resPtr;
4754 register Command *cmdPtr;
4755 Namespace *currNsPtr;
4756 int result;
4757
4758 /*
4759 * Get the internal representation, converting to a command type if
4760 * needed. The internal representation is a ResolvedCmdName that points
4761 * to the actual command.
4762 */
4763
4764 if (objPtr->typePtr != &tclCmdNameType) {
4765 result = tclCmdNameType.setFromAnyProc(interp, objPtr);
4766 if (result != TCL_OK) {
4767 return (Tcl_Command) NULL;
4768 }
4769 }
4770 resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
4771
4772 /*
4773 * Get the current namespace.
4774 */
4775
4776 if (iPtr->varFramePtr != NULL) {
4777 currNsPtr = iPtr->varFramePtr->nsPtr;
4778 } else {
4779 currNsPtr = iPtr->globalNsPtr;
4780 }
4781
4782 /*
4783 * Check the context namespace and the namespace epoch of the resolved
4784 * symbol to make sure that it is fresh. If not, then force another
4785 * conversion to the command type, to discard the old rep and create a
4786 * new one. Note that we verify that the namespace id of the context
4787 * namespace is the same as the one we cached; this insures that the
4788 * namespace wasn't deleted and a new one created at the same address
4789 * with the same command epoch.
4790 */
4791
4792 cmdPtr = NULL;
4793 if ((resPtr != NULL)
4794 && (resPtr->refNsPtr == currNsPtr)
4795 && (resPtr->refNsId == currNsPtr->nsId)
4796 && (resPtr->refNsCmdEpoch == currNsPtr->cmdRefEpoch)) {
4797 cmdPtr = resPtr->cmdPtr;
4798 if (cmdPtr->cmdEpoch != resPtr->cmdEpoch) {
4799 cmdPtr = NULL;
4800 }
4801 }
4802
4803 if (cmdPtr == NULL) {
4804 result = tclCmdNameType.setFromAnyProc(interp, objPtr);
4805 if (result != TCL_OK) {
4806 return (Tcl_Command) NULL;
4807 }
4808 resPtr = (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
4809 if (resPtr != NULL) {
4810 cmdPtr = resPtr->cmdPtr;
4811 }
4812 }
4813 return (Tcl_Command) cmdPtr;
4814 }
4815
4816 /*
4817 *----------------------------------------------------------------------
4818 *
4819 * TclSetCmdNameObj --
4820 *
4821 * Modify an object to be an CmdName object that refers to the argument
4822 * Command structure.
4823 *
4824 * Results:
4825 * None.
4826 *
4827 * Side effects:
4828 * The object's old internal rep is freed. It's string rep is not
4829 * changed. The refcount in the Command structure is incremented to
4830 * keep it from being freed if the command is later deleted until
4831 * TclExecuteByteCode has a chance to recognize that it was deleted.
4832 *
4833 *----------------------------------------------------------------------
4834 */
4835
4836 void
4837 TclSetCmdNameObj(interp, objPtr, cmdPtr)
4838 Tcl_Interp *interp; /* Points to interpreter containing command
4839 * that should be cached in objPtr. */
4840 register Tcl_Obj *objPtr; /* Points to Tcl object to be changed to
4841 * a CmdName object. */
4842 Command *cmdPtr; /* Points to Command structure that the
4843 * CmdName object should refer to. */
4844 {
4845 Interp *iPtr = (Interp *) interp;
4846 register ResolvedCmdName *resPtr;
4847 Tcl_ObjType *oldTypePtr = objPtr->typePtr;
4848 register Namespace *currNsPtr;
4849
4850 if (oldTypePtr == &tclCmdNameType) {
4851 return;
4852 }
4853
4854 /*
4855 * Get the current namespace.
4856 */
4857
4858 if (iPtr->varFramePtr != NULL) {
4859 currNsPtr = iPtr->varFramePtr->nsPtr;
4860 } else {
4861 currNsPtr = iPtr->globalNsPtr;
4862 }
4863
4864 cmdPtr->refCount++;
4865 resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName));
4866 resPtr->cmdPtr = cmdPtr;
4867 resPtr->refNsPtr = currNsPtr;
4868 resPtr->refNsId = currNsPtr->nsId;
4869 resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch;
4870 resPtr->cmdEpoch = cmdPtr->cmdEpoch;
4871 resPtr->refCount = 1;
4872
4873 if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
4874 oldTypePtr->freeIntRepProc(objPtr);
4875 }
4876 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
4877 objPtr->internalRep.twoPtrValue.ptr2 = NULL;
4878 objPtr->typePtr = &tclCmdNameType;
4879 }
4880
4881 /*
4882 *----------------------------------------------------------------------
4883 *
4884 * FreeCmdNameInternalRep --
4885 *
4886 * Frees the resources associated with a cmdName object's internal
4887 * representation.
4888 *
4889 * Results:
4890 * None.
4891 *
4892 * Side effects:
4893 * Decrements the ref count of any cached ResolvedCmdName structure
4894 * pointed to by the cmdName's internal representation. If this is
4895 * the last use of the ResolvedCmdName, it is freed. This in turn
4896 * decrements the ref count of the Command structure pointed to by
4897 * the ResolvedSymbol, which may free the Command structure.
4898 *
4899 *----------------------------------------------------------------------
4900 */
4901
4902 static void
4903 FreeCmdNameInternalRep(objPtr)
4904 register Tcl_Obj *objPtr; /* CmdName object with internal
4905 * representation to free. */
4906 {
4907 register ResolvedCmdName *resPtr =
4908 (ResolvedCmdName *) objPtr->internalRep.otherValuePtr;
4909
4910 if (resPtr != NULL) {
4911 /*
4912 * Decrement the reference count of the ResolvedCmdName structure.
4913 * If there are no more uses, free the ResolvedCmdName structure.
4914 */
4915
4916 resPtr->refCount--;
4917 if (resPtr->refCount == 0) {
4918 /*
4919 * Now free the cached command, unless it is still in its
4920 * hash table or if there are other references to it
4921 * from other cmdName objects.
4922 */
4923
4924 Command *cmdPtr = resPtr->cmdPtr;
4925 TclCleanupCommand(cmdPtr);
4926 ckfree((char *) resPtr);
4927 }
4928 }
4929 }
4930
4931 /*
4932 *----------------------------------------------------------------------
4933 *
4934 * DupCmdNameInternalRep --
4935 *
4936 * Initialize the internal representation of an cmdName Tcl_Obj to a
4937 * copy of the internal representation of an existing cmdName object.
4938 *
4939 * Results:
4940 * None.
4941 *
4942 * Side effects:
4943 * "copyPtr"s internal rep is set to point to the ResolvedCmdName
4944 * structure corresponding to "srcPtr"s internal rep. Increments the
4945 * ref count of the ResolvedCmdName structure pointed to by the
4946 * cmdName's internal representation.
4947 *
4948 *----------------------------------------------------------------------
4949 */
4950
4951 static void
4952 DupCmdNameInternalRep(srcPtr, copyPtr)
4953 Tcl_Obj *srcPtr; /* Object with internal rep to copy. */
4954 register Tcl_Obj *copyPtr; /* Object with internal rep to set. */
4955 {
4956 register ResolvedCmdName *resPtr =
4957 (ResolvedCmdName *) srcPtr->internalRep.otherValuePtr;
4958
4959 copyPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
4960 copyPtr->internalRep.twoPtrValue.ptr2 = NULL;
4961 if (resPtr != NULL) {
4962 resPtr->refCount++;
4963 }
4964 copyPtr->typePtr = &tclCmdNameType;
4965 }
4966
4967 /*
4968 *----------------------------------------------------------------------
4969 *
4970 * SetCmdNameFromAny --
4971 *
4972 * Generate an cmdName internal form for the Tcl object "objPtr".
4973 *
4974 * Results:
4975 * The return value is a standard Tcl result. The conversion always
4976 * succeeds and TCL_OK is returned.
4977 *
4978 * Side effects:
4979 * A pointer to a ResolvedCmdName structure that holds a cached pointer
4980 * to the command with a name that matches objPtr's string rep is
4981 * stored as objPtr's internal representation. This ResolvedCmdName
4982 * pointer will be NULL if no matching command was found. The ref count
4983 * of the cached Command's structure (if any) is also incremented.
4984 *
4985 *----------------------------------------------------------------------
4986 */
4987
4988 static int
4989 SetCmdNameFromAny(interp, objPtr)
4990 Tcl_Interp *interp; /* Used for error reporting if not NULL. */
4991 register Tcl_Obj *objPtr; /* The object to convert. */
4992 {
4993 Interp *iPtr = (Interp *) interp;
4994 char *name;
4995 Tcl_Command cmd;
4996 register Command *cmdPtr;
4997 Namespace *currNsPtr;
4998 register ResolvedCmdName *resPtr;
4999
5000 /*
5001 * Get "objPtr"s string representation. Make it up-to-date if necessary.
5002 */
5003
5004 name = objPtr->bytes;
5005 if (name == NULL) {
5006 name = Tcl_GetString(objPtr);
5007 }
5008
5009 /*
5010 * Find the Command structure, if any, that describes the command called
5011 * "name". Build a ResolvedCmdName that holds a cached pointer to this
5012 * Command, and bump the reference count in the referenced Command
5013 * structure. A Command structure will not be deleted as long as it is
5014 * referenced from a CmdName object.
5015 */
5016
5017 cmd = Tcl_FindCommand(interp, name, (Tcl_Namespace *) NULL,
5018 /*flags*/ 0);
5019 cmdPtr = (Command *) cmd;
5020 if (cmdPtr != NULL) {
5021 /*
5022 * Get the current namespace.
5023 */
5024
5025 if (iPtr->varFramePtr != NULL) {
5026 currNsPtr = iPtr->varFramePtr->nsPtr;
5027 } else {
5028 currNsPtr = iPtr->globalNsPtr;
5029 }
5030
5031 cmdPtr->refCount++;
5032 resPtr = (ResolvedCmdName *) ckalloc(sizeof(ResolvedCmdName));
5033 resPtr->cmdPtr = cmdPtr;
5034 resPtr->refNsPtr = currNsPtr;
5035 resPtr->refNsId = currNsPtr->nsId;
5036 resPtr->refNsCmdEpoch = currNsPtr->cmdRefEpoch;
5037 resPtr->cmdEpoch = cmdPtr->cmdEpoch;
5038 resPtr->refCount = 1;
5039 } else {
5040 resPtr = NULL; /* no command named "name" was found */
5041 }
5042
5043 /*
5044 * Free the old internalRep before setting the new one. We do this as
5045 * late as possible to allow the conversion code, in particular
5046 * GetStringFromObj, to use that old internalRep. If no Command
5047 * structure was found, leave NULL as the cached value.
5048 */
5049
5050 if ((objPtr->typePtr != NULL)
5051 && (objPtr->typePtr->freeIntRepProc != NULL)) {
5052 objPtr->typePtr->freeIntRepProc(objPtr);
5053 }
5054
5055 objPtr->internalRep.twoPtrValue.ptr1 = (VOID *) resPtr;
5056 objPtr->internalRep.twoPtrValue.ptr2 = NULL;
5057 objPtr->typePtr = &tclCmdNameType;
5058 return TCL_OK;
5059 }
5060
5061 #ifdef TCL_COMPILE_DEBUG
5062 /*
5063 *----------------------------------------------------------------------
5064 *
5065 * StringForResultCode --
5066 *
5067 * Procedure that returns a human-readable string representing a
5068 * Tcl result code such as TCL_ERROR.
5069 *
5070 * Results:
5071 * If the result code is one of the standard Tcl return codes, the
5072 * result is a string representing that code such as "TCL_ERROR".
5073 * Otherwise, the result string is that code formatted as a
5074 * sequence of decimal digit characters. Note that the resulting
5075 * string must not be modified by the caller.
5076 *
5077 * Side effects:
5078 * None.
5079 *
5080 *----------------------------------------------------------------------
5081 */
5082
5083 static char *
5084 StringForResultCode(result)
5085 int result; /* The Tcl result code for which to
5086 * generate a string. */
5087 {
5088 static char buf[TCL_INTEGER_SPACE];
5089
5090 if ((result >= TCL_OK) && (result <= TCL_CONTINUE)) {
5091 return resultStrings[result];
5092 }
5093 TclFormatInt(buf, result);
5094 return buf;
5095 }
5096 #endif /* TCL_COMPILE_DEBUG */
5097
5098
5099 /* $History: tclexecute.c $
5100 *
5101 * ***************** Version 1 *****************
5102 * User: Dtashley Date: 1/02/01 Time: 1:31a
5103 * Created in $/IjuScripter, IjuConsole/Source/Tcl Base
5104 * Initial check-in.
5105 */
5106
5107 /* End of TCLEXECUTE.C */

dashley@gmail.com
ViewVC Help
Powered by ViewVC 1.1.25