1 |
/* $Header$ */ |
2 |
/* |
3 |
* tclCompile.c -- |
4 |
* |
5 |
* This file contains procedures that compile Tcl commands or parts |
6 |
* of commands (like quoted strings or nested sub-commands) into a |
7 |
* sequence of instructions ("bytecodes"). |
8 |
* |
9 |
* Copyright (c) 1996-1998 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: tclcompile.c,v 1.1.1.1 2001/06/13 04:36:17 dtashley Exp $ |
15 |
*/ |
16 |
|
17 |
#include "tclInt.h" |
18 |
#include "tclCompile.h" |
19 |
|
20 |
/* |
21 |
* Table of all AuxData types. |
22 |
*/ |
23 |
|
24 |
static Tcl_HashTable auxDataTypeTable; |
25 |
static int auxDataTypeTableInitialized; /* 0 means not yet initialized. */ |
26 |
|
27 |
TCL_DECLARE_MUTEX(tableMutex) |
28 |
|
29 |
/* |
30 |
* Variable that controls whether compilation tracing is enabled and, if so, |
31 |
* what level of tracing is desired: |
32 |
* 0: no compilation tracing |
33 |
* 1: summarize compilation of top level cmds and proc bodies |
34 |
* 2: display all instructions of each ByteCode compiled |
35 |
* This variable is linked to the Tcl variable "tcl_traceCompile". |
36 |
*/ |
37 |
|
38 |
int tclTraceCompile = 0; |
39 |
static int traceInitialized = 0; |
40 |
|
41 |
/* |
42 |
* A table describing the Tcl bytecode instructions. Entries in this table |
43 |
* must correspond to the instruction opcode definitions in tclCompile.h. |
44 |
* The names "op1" and "op4" refer to an instruction's one or four byte |
45 |
* first operand. Similarly, "stktop" and "stknext" refer to the topmost |
46 |
* and next to topmost stack elements. |
47 |
* |
48 |
* Note that the load, store, and incr instructions do not distinguish local |
49 |
* from global variables; the bytecode interpreter at runtime uses the |
50 |
* existence of a procedure call frame to distinguish these. |
51 |
*/ |
52 |
|
53 |
InstructionDesc instructionTable[] = { |
54 |
/* Name Bytes #Opnds Operand types Stack top, next */ |
55 |
{"done", 1, 0, {OPERAND_NONE}}, |
56 |
/* Finish ByteCode execution and return stktop (top stack item) */ |
57 |
{"push1", 2, 1, {OPERAND_UINT1}}, |
58 |
/* Push object at ByteCode objArray[op1] */ |
59 |
{"push4", 5, 1, {OPERAND_UINT4}}, |
60 |
/* Push object at ByteCode objArray[op4] */ |
61 |
{"pop", 1, 0, {OPERAND_NONE}}, |
62 |
/* Pop the topmost stack object */ |
63 |
{"dup", 1, 0, {OPERAND_NONE}}, |
64 |
/* Duplicate the topmost stack object and push the result */ |
65 |
{"concat1", 2, 1, {OPERAND_UINT1}}, |
66 |
/* Concatenate the top op1 items and push result */ |
67 |
{"invokeStk1", 2, 1, {OPERAND_UINT1}}, |
68 |
/* Invoke command named objv[0]; <objc,objv> = <op1,top op1> */ |
69 |
{"invokeStk4", 5, 1, {OPERAND_UINT4}}, |
70 |
/* Invoke command named objv[0]; <objc,objv> = <op4,top op4> */ |
71 |
{"evalStk", 1, 0, {OPERAND_NONE}}, |
72 |
/* Evaluate command in stktop using Tcl_EvalObj. */ |
73 |
{"exprStk", 1, 0, {OPERAND_NONE}}, |
74 |
/* Execute expression in stktop using Tcl_ExprStringObj. */ |
75 |
|
76 |
{"loadScalar1", 2, 1, {OPERAND_UINT1}}, |
77 |
/* Load scalar variable at index op1 <= 255 in call frame */ |
78 |
{"loadScalar4", 5, 1, {OPERAND_UINT4}}, |
79 |
/* Load scalar variable at index op1 >= 256 in call frame */ |
80 |
{"loadScalarStk", 1, 0, {OPERAND_NONE}}, |
81 |
/* Load scalar variable; scalar's name is stktop */ |
82 |
{"loadArray1", 2, 1, {OPERAND_UINT1}}, |
83 |
/* Load array element; array at slot op1<=255, element is stktop */ |
84 |
{"loadArray4", 5, 1, {OPERAND_UINT4}}, |
85 |
/* Load array element; array at slot op1 > 255, element is stktop */ |
86 |
{"loadArrayStk", 1, 0, {OPERAND_NONE}}, |
87 |
/* Load array element; element is stktop, array name is stknext */ |
88 |
{"loadStk", 1, 0, {OPERAND_NONE}}, |
89 |
/* Load general variable; unparsed variable name is stktop */ |
90 |
{"storeScalar1", 2, 1, {OPERAND_UINT1}}, |
91 |
/* Store scalar variable at op1<=255 in frame; value is stktop */ |
92 |
{"storeScalar4", 5, 1, {OPERAND_UINT4}}, |
93 |
/* Store scalar variable at op1 > 255 in frame; value is stktop */ |
94 |
{"storeScalarStk", 1, 0, {OPERAND_NONE}}, |
95 |
/* Store scalar; value is stktop, scalar name is stknext */ |
96 |
{"storeArray1", 2, 1, {OPERAND_UINT1}}, |
97 |
/* Store array element; array at op1<=255, value is top then elem */ |
98 |
{"storeArray4", 5, 1, {OPERAND_UINT4}}, |
99 |
/* Store array element; array at op1>=256, value is top then elem */ |
100 |
{"storeArrayStk", 1, 0, {OPERAND_NONE}}, |
101 |
/* Store array element; value is stktop, then elem, array names */ |
102 |
{"storeStk", 1, 0, {OPERAND_NONE}}, |
103 |
/* Store general variable; value is stktop, then unparsed name */ |
104 |
|
105 |
{"incrScalar1", 2, 1, {OPERAND_UINT1}}, |
106 |
/* Incr scalar at index op1<=255 in frame; incr amount is stktop */ |
107 |
{"incrScalarStk", 1, 0, {OPERAND_NONE}}, |
108 |
/* Incr scalar; incr amount is stktop, scalar's name is stknext */ |
109 |
{"incrArray1", 2, 1, {OPERAND_UINT1}}, |
110 |
/* Incr array elem; arr at slot op1<=255, amount is top then elem */ |
111 |
{"incrArrayStk", 1, 0, {OPERAND_NONE}}, |
112 |
/* Incr array element; amount is top then elem then array names */ |
113 |
{"incrStk", 1, 0, {OPERAND_NONE}}, |
114 |
/* Incr general variable; amount is stktop then unparsed var name */ |
115 |
{"incrScalar1Imm", 3, 2, {OPERAND_UINT1, OPERAND_INT1}}, |
116 |
/* Incr scalar at slot op1 <= 255; amount is 2nd operand byte */ |
117 |
{"incrScalarStkImm", 2, 1, {OPERAND_INT1}}, |
118 |
/* Incr scalar; scalar name is stktop; incr amount is op1 */ |
119 |
{"incrArray1Imm", 3, 2, {OPERAND_UINT1, OPERAND_INT1}}, |
120 |
/* Incr array elem; array at slot op1 <= 255, elem is stktop, |
121 |
* amount is 2nd operand byte */ |
122 |
{"incrArrayStkImm", 2, 1, {OPERAND_INT1}}, |
123 |
/* Incr array element; elem is top then array name, amount is op1 */ |
124 |
{"incrStkImm", 2, 1, {OPERAND_INT1}}, |
125 |
/* Incr general variable; unparsed name is top, amount is op1 */ |
126 |
|
127 |
{"jump1", 2, 1, {OPERAND_INT1}}, |
128 |
/* Jump relative to (pc + op1) */ |
129 |
{"jump4", 5, 1, {OPERAND_INT4}}, |
130 |
/* Jump relative to (pc + op4) */ |
131 |
{"jumpTrue1", 2, 1, {OPERAND_INT1}}, |
132 |
/* Jump relative to (pc + op1) if stktop expr object is true */ |
133 |
{"jumpTrue4", 5, 1, {OPERAND_INT4}}, |
134 |
/* Jump relative to (pc + op4) if stktop expr object is true */ |
135 |
{"jumpFalse1", 2, 1, {OPERAND_INT1}}, |
136 |
/* Jump relative to (pc + op1) if stktop expr object is false */ |
137 |
{"jumpFalse4", 5, 1, {OPERAND_INT4}}, |
138 |
/* Jump relative to (pc + op4) if stktop expr object is false */ |
139 |
|
140 |
{"lor", 1, 0, {OPERAND_NONE}}, |
141 |
/* Logical or: push (stknext || stktop) */ |
142 |
{"land", 1, 0, {OPERAND_NONE}}, |
143 |
/* Logical and: push (stknext && stktop) */ |
144 |
{"bitor", 1, 0, {OPERAND_NONE}}, |
145 |
/* Bitwise or: push (stknext | stktop) */ |
146 |
{"bitxor", 1, 0, {OPERAND_NONE}}, |
147 |
/* Bitwise xor push (stknext ^ stktop) */ |
148 |
{"bitand", 1, 0, {OPERAND_NONE}}, |
149 |
/* Bitwise and: push (stknext & stktop) */ |
150 |
{"eq", 1, 0, {OPERAND_NONE}}, |
151 |
/* Equal: push (stknext == stktop) */ |
152 |
{"neq", 1, 0, {OPERAND_NONE}}, |
153 |
/* Not equal: push (stknext != stktop) */ |
154 |
{"lt", 1, 0, {OPERAND_NONE}}, |
155 |
/* Less: push (stknext < stktop) */ |
156 |
{"gt", 1, 0, {OPERAND_NONE}}, |
157 |
/* Greater: push (stknext || stktop) */ |
158 |
{"le", 1, 0, {OPERAND_NONE}}, |
159 |
/* Logical or: push (stknext || stktop) */ |
160 |
{"ge", 1, 0, {OPERAND_NONE}}, |
161 |
/* Logical or: push (stknext || stktop) */ |
162 |
{"lshift", 1, 0, {OPERAND_NONE}}, |
163 |
/* Left shift: push (stknext << stktop) */ |
164 |
{"rshift", 1, 0, {OPERAND_NONE}}, |
165 |
/* Right shift: push (stknext >> stktop) */ |
166 |
{"add", 1, 0, {OPERAND_NONE}}, |
167 |
/* Add: push (stknext + stktop) */ |
168 |
{"sub", 1, 0, {OPERAND_NONE}}, |
169 |
/* Sub: push (stkext - stktop) */ |
170 |
{"mult", 1, 0, {OPERAND_NONE}}, |
171 |
/* Multiply: push (stknext * stktop) */ |
172 |
{"div", 1, 0, {OPERAND_NONE}}, |
173 |
/* Divide: push (stknext / stktop) */ |
174 |
{"mod", 1, 0, {OPERAND_NONE}}, |
175 |
/* Mod: push (stknext % stktop) */ |
176 |
{"uplus", 1, 0, {OPERAND_NONE}}, |
177 |
/* Unary plus: push +stktop */ |
178 |
{"uminus", 1, 0, {OPERAND_NONE}}, |
179 |
/* Unary minus: push -stktop */ |
180 |
{"bitnot", 1, 0, {OPERAND_NONE}}, |
181 |
/* Bitwise not: push ~stktop */ |
182 |
{"not", 1, 0, {OPERAND_NONE}}, |
183 |
/* Logical not: push !stktop */ |
184 |
{"callBuiltinFunc1", 2, 1, {OPERAND_UINT1}}, |
185 |
/* Call builtin math function with index op1; any args are on stk */ |
186 |
{"callFunc1", 2, 1, {OPERAND_UINT1}}, |
187 |
/* Call non-builtin func objv[0]; <objc,objv>=<op1,top op1> */ |
188 |
{"tryCvtToNumeric", 1, 0, {OPERAND_NONE}}, |
189 |
/* Try converting stktop to first int then double if possible. */ |
190 |
|
191 |
{"break", 1, 0, {OPERAND_NONE}}, |
192 |
/* Abort closest enclosing loop; if none, return TCL_BREAK code. */ |
193 |
{"continue", 1, 0, {OPERAND_NONE}}, |
194 |
/* Skip to next iteration of closest enclosing loop; if none, |
195 |
* return TCL_CONTINUE code. */ |
196 |
|
197 |
{"foreach_start4", 5, 1, {OPERAND_UINT4}}, |
198 |
/* Initialize execution of a foreach loop. Operand is aux data index |
199 |
* of the ForeachInfo structure for the foreach command. */ |
200 |
{"foreach_step4", 5, 1, {OPERAND_UINT4}}, |
201 |
/* "Step" or begin next iteration of foreach loop. Push 0 if to |
202 |
* terminate loop, else push 1. */ |
203 |
|
204 |
{"beginCatch4", 5, 1, {OPERAND_UINT4}}, |
205 |
/* Record start of catch with the operand's exception index. |
206 |
* Push the current stack depth onto a special catch stack. */ |
207 |
{"endCatch", 1, 0, {OPERAND_NONE}}, |
208 |
/* End of last catch. Pop the bytecode interpreter's catch stack. */ |
209 |
{"pushResult", 1, 0, {OPERAND_NONE}}, |
210 |
/* Push the interpreter's object result onto the stack. */ |
211 |
{"pushReturnCode", 1, 0, {OPERAND_NONE}}, |
212 |
/* Push interpreter's return code (e.g. TCL_OK or TCL_ERROR) as |
213 |
* a new object onto the stack. */ |
214 |
{0} |
215 |
}; |
216 |
|
217 |
/* |
218 |
* Prototypes for procedures defined later in this file: |
219 |
*/ |
220 |
|
221 |
static void DupByteCodeInternalRep _ANSI_ARGS_((Tcl_Obj *srcPtr, |
222 |
Tcl_Obj *copyPtr)); |
223 |
static unsigned char * EncodeCmdLocMap _ANSI_ARGS_(( |
224 |
CompileEnv *envPtr, ByteCode *codePtr, |
225 |
unsigned char *startPtr)); |
226 |
static void EnterCmdExtentData _ANSI_ARGS_(( |
227 |
CompileEnv *envPtr, int cmdNumber, |
228 |
int numSrcBytes, int numCodeBytes)); |
229 |
static void EnterCmdStartData _ANSI_ARGS_(( |
230 |
CompileEnv *envPtr, int cmdNumber, |
231 |
int srcOffset, int codeOffset)); |
232 |
static void FreeByteCodeInternalRep _ANSI_ARGS_(( |
233 |
Tcl_Obj *objPtr)); |
234 |
static int GetCmdLocEncodingSize _ANSI_ARGS_(( |
235 |
CompileEnv *envPtr)); |
236 |
static void LogCompilationInfo _ANSI_ARGS_((Tcl_Interp *interp, |
237 |
char *script, char *command, int length)); |
238 |
#ifdef TCL_COMPILE_STATS |
239 |
static void RecordByteCodeStats _ANSI_ARGS_(( |
240 |
ByteCode *codePtr)); |
241 |
#endif /* TCL_COMPILE_STATS */ |
242 |
static int SetByteCodeFromAny _ANSI_ARGS_((Tcl_Interp *interp, |
243 |
Tcl_Obj *objPtr)); |
244 |
|
245 |
/* |
246 |
* The structure below defines the bytecode Tcl object type by |
247 |
* means of procedures that can be invoked by generic object code. |
248 |
*/ |
249 |
|
250 |
Tcl_ObjType tclByteCodeType = { |
251 |
"bytecode", /* name */ |
252 |
FreeByteCodeInternalRep, /* freeIntRepProc */ |
253 |
DupByteCodeInternalRep, /* dupIntRepProc */ |
254 |
(Tcl_UpdateStringProc *) NULL, /* updateStringProc */ |
255 |
SetByteCodeFromAny /* setFromAnyProc */ |
256 |
}; |
257 |
|
258 |
/* |
259 |
*---------------------------------------------------------------------- |
260 |
* |
261 |
* TclSetByteCodeFromAny -- |
262 |
* |
263 |
* Part of the bytecode Tcl object type implementation. Attempts to |
264 |
* generate an byte code internal form for the Tcl object "objPtr" by |
265 |
* compiling its string representation. This function also takes |
266 |
* a hook procedure that will be invoked to perform any needed post |
267 |
* processing on the compilation results before generating byte |
268 |
* codes. |
269 |
* |
270 |
* Results: |
271 |
* The return value is a standard Tcl object result. If an error occurs |
272 |
* during compilation, an error message is left in the interpreter's |
273 |
* result unless "interp" is NULL. |
274 |
* |
275 |
* Side effects: |
276 |
* Frees the old internal representation. If no error occurs, then the |
277 |
* compiled code is stored as "objPtr"s bytecode representation. |
278 |
* Also, if debugging, initializes the "tcl_traceCompile" Tcl variable |
279 |
* used to trace compilations. |
280 |
* |
281 |
*---------------------------------------------------------------------- |
282 |
*/ |
283 |
|
284 |
int |
285 |
TclSetByteCodeFromAny(interp, objPtr, hookProc, clientData) |
286 |
Tcl_Interp *interp; /* The interpreter for which the code is |
287 |
* being compiled. Must not be NULL. */ |
288 |
Tcl_Obj *objPtr; /* The object to make a ByteCode object. */ |
289 |
CompileHookProc *hookProc; /* Procedure to invoke after compilation. */ |
290 |
ClientData clientData; /* Hook procedure private data. */ |
291 |
{ |
292 |
Interp *iPtr = (Interp *) interp; |
293 |
CompileEnv compEnv; /* Compilation environment structure |
294 |
* allocated in frame. */ |
295 |
LiteralTable *localTablePtr = &(compEnv.localLitTable); |
296 |
register AuxData *auxDataPtr; |
297 |
LiteralEntry *entryPtr; |
298 |
register int i; |
299 |
int length, nested, result; |
300 |
char *string; |
301 |
|
302 |
if (!traceInitialized) { |
303 |
if (Tcl_LinkVar(interp, "tcl_traceCompile", |
304 |
(char *) &tclTraceCompile, TCL_LINK_INT) != TCL_OK) { |
305 |
panic("SetByteCodeFromAny: unable to create link for tcl_traceCompile variable"); |
306 |
} |
307 |
traceInitialized = 1; |
308 |
} |
309 |
|
310 |
if (iPtr->evalFlags & TCL_BRACKET_TERM) { |
311 |
nested = 1; |
312 |
} else { |
313 |
nested = 0; |
314 |
} |
315 |
string = Tcl_GetStringFromObj(objPtr, &length); |
316 |
TclInitCompileEnv(interp, &compEnv, string, length); |
317 |
result = TclCompileScript(interp, string, length, nested, &compEnv); |
318 |
|
319 |
if (result == TCL_OK) { |
320 |
/* |
321 |
* Successful compilation. Add a "done" instruction at the end. |
322 |
*/ |
323 |
|
324 |
compEnv.numSrcBytes = iPtr->termOffset; |
325 |
TclEmitOpcode(INST_DONE, &compEnv); |
326 |
|
327 |
/* |
328 |
* Invoke the compilation hook procedure if one exists. |
329 |
*/ |
330 |
|
331 |
if (hookProc) { |
332 |
result = (*hookProc)(interp, &compEnv, clientData); |
333 |
} |
334 |
|
335 |
/* |
336 |
* Change the object into a ByteCode object. Ownership of the literal |
337 |
* objects and aux data items is given to the ByteCode object. |
338 |
*/ |
339 |
|
340 |
#ifdef TCL_COMPILE_DEBUG |
341 |
TclVerifyLocalLiteralTable(&compEnv); |
342 |
#endif /*TCL_COMPILE_DEBUG*/ |
343 |
|
344 |
TclInitByteCodeObj(objPtr, &compEnv); |
345 |
#ifdef TCL_COMPILE_DEBUG |
346 |
if (tclTraceCompile == 2) { |
347 |
TclPrintByteCodeObj(interp, objPtr); |
348 |
} |
349 |
#endif /* TCL_COMPILE_DEBUG */ |
350 |
} |
351 |
|
352 |
if (result != TCL_OK) { |
353 |
/* |
354 |
* Compilation errors. |
355 |
*/ |
356 |
|
357 |
entryPtr = compEnv.literalArrayPtr; |
358 |
for (i = 0; i < compEnv.literalArrayNext; i++) { |
359 |
TclReleaseLiteral(interp, entryPtr->objPtr); |
360 |
entryPtr++; |
361 |
} |
362 |
#ifdef TCL_COMPILE_DEBUG |
363 |
TclVerifyGlobalLiteralTable(iPtr); |
364 |
#endif /*TCL_COMPILE_DEBUG*/ |
365 |
|
366 |
auxDataPtr = compEnv.auxDataArrayPtr; |
367 |
for (i = 0; i < compEnv.auxDataArrayNext; i++) { |
368 |
if (auxDataPtr->type->freeProc != NULL) { |
369 |
auxDataPtr->type->freeProc(auxDataPtr->clientData); |
370 |
} |
371 |
auxDataPtr++; |
372 |
} |
373 |
} |
374 |
|
375 |
|
376 |
/* |
377 |
* Free storage allocated during compilation. |
378 |
*/ |
379 |
|
380 |
if (localTablePtr->buckets != localTablePtr->staticBuckets) { |
381 |
ckfree((char *) localTablePtr->buckets); |
382 |
} |
383 |
TclFreeCompileEnv(&compEnv); |
384 |
return result; |
385 |
} |
386 |
|
387 |
/* |
388 |
*----------------------------------------------------------------------- |
389 |
* |
390 |
* SetByteCodeFromAny -- |
391 |
* |
392 |
* Part of the bytecode Tcl object type implementation. Attempts to |
393 |
* generate an byte code internal form for the Tcl object "objPtr" by |
394 |
* compiling its string representation. |
395 |
* |
396 |
* Results: |
397 |
* The return value is a standard Tcl object result. If an error occurs |
398 |
* during compilation, an error message is left in the interpreter's |
399 |
* result unless "interp" is NULL. |
400 |
* |
401 |
* Side effects: |
402 |
* Frees the old internal representation. If no error occurs, then the |
403 |
* compiled code is stored as "objPtr"s bytecode representation. |
404 |
* Also, if debugging, initializes the "tcl_traceCompile" Tcl variable |
405 |
* used to trace compilations. |
406 |
* |
407 |
*---------------------------------------------------------------------- |
408 |
*/ |
409 |
|
410 |
static int |
411 |
SetByteCodeFromAny(interp, objPtr) |
412 |
Tcl_Interp *interp; /* The interpreter for which the code is |
413 |
* being compiled. Must not be NULL. */ |
414 |
Tcl_Obj *objPtr; /* The object to make a ByteCode object. */ |
415 |
{ |
416 |
return TclSetByteCodeFromAny(interp, objPtr, |
417 |
(CompileHookProc *) NULL, (ClientData) NULL); |
418 |
} |
419 |
|
420 |
/* |
421 |
*---------------------------------------------------------------------- |
422 |
* |
423 |
* DupByteCodeInternalRep -- |
424 |
* |
425 |
* Part of the bytecode Tcl object type implementation. However, it |
426 |
* does not copy the internal representation of a bytecode Tcl_Obj, but |
427 |
* instead leaves the new object untyped (with a NULL type pointer). |
428 |
* Code will be compiled for the new object only if necessary. |
429 |
* |
430 |
* Results: |
431 |
* None. |
432 |
* |
433 |
* Side effects: |
434 |
* None. |
435 |
* |
436 |
*---------------------------------------------------------------------- |
437 |
*/ |
438 |
|
439 |
static void |
440 |
DupByteCodeInternalRep(srcPtr, copyPtr) |
441 |
Tcl_Obj *srcPtr; /* Object with internal rep to copy. */ |
442 |
Tcl_Obj *copyPtr; /* Object with internal rep to set. */ |
443 |
{ |
444 |
return; |
445 |
} |
446 |
|
447 |
/* |
448 |
*---------------------------------------------------------------------- |
449 |
* |
450 |
* FreeByteCodeInternalRep -- |
451 |
* |
452 |
* Part of the bytecode Tcl object type implementation. Frees the |
453 |
* storage associated with a bytecode object's internal representation |
454 |
* unless its code is actively being executed. |
455 |
* |
456 |
* Results: |
457 |
* None. |
458 |
* |
459 |
* Side effects: |
460 |
* The bytecode object's internal rep is marked invalid and its |
461 |
* code gets freed unless the code is actively being executed. |
462 |
* In that case the cleanup is delayed until the last execution |
463 |
* of the code completes. |
464 |
* |
465 |
*---------------------------------------------------------------------- |
466 |
*/ |
467 |
|
468 |
static void |
469 |
FreeByteCodeInternalRep(objPtr) |
470 |
register Tcl_Obj *objPtr; /* Object whose internal rep to free. */ |
471 |
{ |
472 |
register ByteCode *codePtr = |
473 |
(ByteCode *) objPtr->internalRep.otherValuePtr; |
474 |
|
475 |
codePtr->refCount--; |
476 |
if (codePtr->refCount <= 0) { |
477 |
TclCleanupByteCode(codePtr); |
478 |
} |
479 |
objPtr->typePtr = NULL; |
480 |
objPtr->internalRep.otherValuePtr = NULL; |
481 |
} |
482 |
|
483 |
/* |
484 |
*---------------------------------------------------------------------- |
485 |
* |
486 |
* TclCleanupByteCode -- |
487 |
* |
488 |
* This procedure does all the real work of freeing up a bytecode |
489 |
* object's ByteCode structure. It's called only when the structure's |
490 |
* reference count becomes zero. |
491 |
* |
492 |
* Results: |
493 |
* None. |
494 |
* |
495 |
* Side effects: |
496 |
* Frees objPtr's bytecode internal representation and sets its type |
497 |
* and objPtr->internalRep.otherValuePtr NULL. Also releases its |
498 |
* literals and frees its auxiliary data items. |
499 |
* |
500 |
*---------------------------------------------------------------------- |
501 |
*/ |
502 |
|
503 |
void |
504 |
TclCleanupByteCode(codePtr) |
505 |
register ByteCode *codePtr; /* Points to the ByteCode to free. */ |
506 |
{ |
507 |
Tcl_Interp *interp = (Tcl_Interp *) *codePtr->interpHandle; |
508 |
int numLitObjects = codePtr->numLitObjects; |
509 |
int numAuxDataItems = codePtr->numAuxDataItems; |
510 |
register Tcl_Obj **objArrayPtr; |
511 |
register AuxData *auxDataPtr; |
512 |
int i; |
513 |
#ifdef TCL_COMPILE_STATS |
514 |
|
515 |
if (interp != NULL) { |
516 |
ByteCodeStats *statsPtr; |
517 |
Tcl_Time destroyTime; |
518 |
int lifetimeSec, lifetimeMicroSec, log2; |
519 |
|
520 |
statsPtr = &((Interp *) interp)->stats; |
521 |
|
522 |
statsPtr->numByteCodesFreed++; |
523 |
statsPtr->currentSrcBytes -= (double) codePtr->numSrcBytes; |
524 |
statsPtr->currentByteCodeBytes -= (double) codePtr->structureSize; |
525 |
|
526 |
statsPtr->currentInstBytes -= (double) codePtr->numCodeBytes; |
527 |
statsPtr->currentLitBytes -= |
528 |
(double) (codePtr->numLitObjects * sizeof(Tcl_Obj *)); |
529 |
statsPtr->currentExceptBytes -= |
530 |
(double) (codePtr->numExceptRanges * sizeof(ExceptionRange)); |
531 |
statsPtr->currentAuxBytes -= |
532 |
(double) (codePtr->numAuxDataItems * sizeof(AuxData)); |
533 |
statsPtr->currentCmdMapBytes -= (double) codePtr->numCmdLocBytes; |
534 |
|
535 |
TclpGetTime(&destroyTime); |
536 |
lifetimeSec = destroyTime.sec - codePtr->createTime.sec; |
537 |
if (lifetimeSec > 2000) { /* avoid overflow */ |
538 |
lifetimeSec = 2000; |
539 |
} |
540 |
lifetimeMicroSec = |
541 |
1000000*lifetimeSec + (destroyTime.usec - codePtr->createTime.usec); |
542 |
|
543 |
log2 = TclLog2(lifetimeMicroSec); |
544 |
if (log2 > 31) { |
545 |
log2 = 31; |
546 |
} |
547 |
statsPtr->lifetimeCount[log2]++; |
548 |
} |
549 |
#endif /* TCL_COMPILE_STATS */ |
550 |
|
551 |
/* |
552 |
* A single heap object holds the ByteCode structure and its code, |
553 |
* object, command location, and auxiliary data arrays. This means we |
554 |
* only need to 1) decrement the ref counts of the LiteralEntry's in |
555 |
* its literal array, 2) call the free procs for the auxiliary data |
556 |
* items, and 3) free the ByteCode structure's heap object. |
557 |
* |
558 |
* The case for TCL_BYTECODE_PRECOMPILED (precompiled ByteCodes, |
559 |
* like those generated from tbcload) is special, as they doesn't |
560 |
* make use of the global literal table. They instead maintain |
561 |
* private references to their literals which must be decremented. |
562 |
*/ |
563 |
|
564 |
if (codePtr->flags & TCL_BYTECODE_PRECOMPILED) { |
565 |
register Tcl_Obj *objPtr; |
566 |
|
567 |
objArrayPtr = codePtr->objArrayPtr; |
568 |
for (i = 0; i < numLitObjects; i++) { |
569 |
objPtr = *objArrayPtr; |
570 |
if (objPtr) { |
571 |
Tcl_DecrRefCount(objPtr); |
572 |
} |
573 |
objArrayPtr++; |
574 |
} |
575 |
codePtr->numLitObjects = 0; |
576 |
} else if (interp != NULL) { |
577 |
/* |
578 |
* If the interp has already been freed, then Tcl will have already |
579 |
* forcefully released all the literals used by ByteCodes compiled |
580 |
* with respect to that interp. |
581 |
*/ |
582 |
|
583 |
objArrayPtr = codePtr->objArrayPtr; |
584 |
for (i = 0; i < numLitObjects; i++) { |
585 |
/* |
586 |
* TclReleaseLiteral sets a ByteCode's object array entry NULL to |
587 |
* indicate that it has already freed the literal. |
588 |
*/ |
589 |
|
590 |
if (*objArrayPtr != NULL) { |
591 |
TclReleaseLiteral(interp, *objArrayPtr); |
592 |
} |
593 |
objArrayPtr++; |
594 |
} |
595 |
} |
596 |
|
597 |
auxDataPtr = codePtr->auxDataArrayPtr; |
598 |
for (i = 0; i < numAuxDataItems; i++) { |
599 |
if (auxDataPtr->type->freeProc != NULL) { |
600 |
(*auxDataPtr->type->freeProc)(auxDataPtr->clientData); |
601 |
} |
602 |
auxDataPtr++; |
603 |
} |
604 |
|
605 |
TclHandleRelease(codePtr->interpHandle); |
606 |
ckfree((char *) codePtr); |
607 |
} |
608 |
|
609 |
/* |
610 |
*---------------------------------------------------------------------- |
611 |
* |
612 |
* TclInitCompileEnv -- |
613 |
* |
614 |
* Initializes a CompileEnv compilation environment structure for the |
615 |
* compilation of a string in an interpreter. |
616 |
* |
617 |
* Results: |
618 |
* None. |
619 |
* |
620 |
* Side effects: |
621 |
* The CompileEnv structure is initialized. |
622 |
* |
623 |
*---------------------------------------------------------------------- |
624 |
*/ |
625 |
|
626 |
void |
627 |
TclInitCompileEnv(interp, envPtr, string, numBytes) |
628 |
Tcl_Interp *interp; /* The interpreter for which a CompileEnv |
629 |
* structure is initialized. */ |
630 |
register CompileEnv *envPtr; /* Points to the CompileEnv structure to |
631 |
* initialize. */ |
632 |
char *string; /* The source string to be compiled. */ |
633 |
int numBytes; /* Number of bytes in source string. */ |
634 |
{ |
635 |
Interp *iPtr = (Interp *) interp; |
636 |
|
637 |
envPtr->iPtr = iPtr; |
638 |
envPtr->source = string; |
639 |
envPtr->numSrcBytes = numBytes; |
640 |
envPtr->procPtr = iPtr->compiledProcPtr; |
641 |
envPtr->numCommands = 0; |
642 |
envPtr->exceptDepth = 0; |
643 |
envPtr->maxExceptDepth = 0; |
644 |
envPtr->maxStackDepth = 0; |
645 |
TclInitLiteralTable(&(envPtr->localLitTable)); |
646 |
envPtr->exprIsJustVarRef = 0; |
647 |
envPtr->exprIsComparison = 0; |
648 |
|
649 |
envPtr->codeStart = envPtr->staticCodeSpace; |
650 |
envPtr->codeNext = envPtr->codeStart; |
651 |
envPtr->codeEnd = (envPtr->codeStart + COMPILEENV_INIT_CODE_BYTES); |
652 |
envPtr->mallocedCodeArray = 0; |
653 |
|
654 |
envPtr->literalArrayPtr = envPtr->staticLiteralSpace; |
655 |
envPtr->literalArrayNext = 0; |
656 |
envPtr->literalArrayEnd = COMPILEENV_INIT_NUM_OBJECTS; |
657 |
envPtr->mallocedLiteralArray = 0; |
658 |
|
659 |
envPtr->exceptArrayPtr = envPtr->staticExceptArraySpace; |
660 |
envPtr->exceptArrayNext = 0; |
661 |
envPtr->exceptArrayEnd = COMPILEENV_INIT_EXCEPT_RANGES; |
662 |
envPtr->mallocedExceptArray = 0; |
663 |
|
664 |
envPtr->cmdMapPtr = envPtr->staticCmdMapSpace; |
665 |
envPtr->cmdMapEnd = COMPILEENV_INIT_CMD_MAP_SIZE; |
666 |
envPtr->mallocedCmdMap = 0; |
667 |
|
668 |
envPtr->auxDataArrayPtr = envPtr->staticAuxDataArraySpace; |
669 |
envPtr->auxDataArrayNext = 0; |
670 |
envPtr->auxDataArrayEnd = COMPILEENV_INIT_AUX_DATA_SIZE; |
671 |
envPtr->mallocedAuxDataArray = 0; |
672 |
} |
673 |
|
674 |
/* |
675 |
*---------------------------------------------------------------------- |
676 |
* |
677 |
* TclFreeCompileEnv -- |
678 |
* |
679 |
* Free the storage allocated in a CompileEnv compilation environment |
680 |
* structure. |
681 |
* |
682 |
* Results: |
683 |
* None. |
684 |
* |
685 |
* Side effects: |
686 |
* Allocated storage in the CompileEnv structure is freed. Note that |
687 |
* its local literal table is not deleted and its literal objects are |
688 |
* not released. In addition, storage referenced by its auxiliary data |
689 |
* items is not freed. This is done so that, when compilation is |
690 |
* successful, "ownership" of these objects and aux data items is |
691 |
* handed over to the corresponding ByteCode structure. |
692 |
* |
693 |
*---------------------------------------------------------------------- |
694 |
*/ |
695 |
|
696 |
void |
697 |
TclFreeCompileEnv(envPtr) |
698 |
register CompileEnv *envPtr; /* Points to the CompileEnv structure. */ |
699 |
{ |
700 |
if (envPtr->mallocedCodeArray) { |
701 |
ckfree((char *) envPtr->codeStart); |
702 |
} |
703 |
if (envPtr->mallocedLiteralArray) { |
704 |
ckfree((char *) envPtr->literalArrayPtr); |
705 |
} |
706 |
if (envPtr->mallocedExceptArray) { |
707 |
ckfree((char *) envPtr->exceptArrayPtr); |
708 |
} |
709 |
if (envPtr->mallocedCmdMap) { |
710 |
ckfree((char *) envPtr->cmdMapPtr); |
711 |
} |
712 |
if (envPtr->mallocedAuxDataArray) { |
713 |
ckfree((char *) envPtr->auxDataArrayPtr); |
714 |
} |
715 |
} |
716 |
|
717 |
/* |
718 |
*---------------------------------------------------------------------- |
719 |
* |
720 |
* TclCompileScript -- |
721 |
* |
722 |
* Compile a Tcl script in a string. |
723 |
* |
724 |
* Results: |
725 |
* The return value is TCL_OK on a successful compilation and TCL_ERROR |
726 |
* on failure. If TCL_ERROR is returned, then the interpreter's result |
727 |
* contains an error message. |
728 |
* |
729 |
* interp->termOffset is set to the offset of the character in the |
730 |
* script just after the last one successfully processed; this will be |
731 |
* the offset of the ']' if (flags & TCL_BRACKET_TERM). |
732 |
* envPtr->maxStackDepth is set to the maximum number of stack elements |
733 |
* needed to execute the script's commands. |
734 |
* |
735 |
* Side effects: |
736 |
* Adds instructions to envPtr to evaluate the script at runtime. |
737 |
* |
738 |
*---------------------------------------------------------------------- |
739 |
*/ |
740 |
|
741 |
int |
742 |
TclCompileScript(interp, script, numBytes, nested, envPtr) |
743 |
Tcl_Interp *interp; /* Used for error and status reporting. */ |
744 |
char *script; /* The source script to compile. */ |
745 |
int numBytes; /* Number of bytes in script. If < 0, the |
746 |
* script consists of all bytes up to the |
747 |
* first null character. */ |
748 |
int nested; /* Non-zero means this is a nested command: |
749 |
* close bracket ']' should be considered a |
750 |
* command terminator. If zero, close |
751 |
* bracket has no special meaning. */ |
752 |
CompileEnv *envPtr; /* Holds resulting instructions. */ |
753 |
{ |
754 |
Interp *iPtr = (Interp *) interp; |
755 |
Tcl_Parse parse; |
756 |
int maxDepth = 0; /* Maximum number of stack elements needed |
757 |
* to execute all cmds. */ |
758 |
int lastTopLevelCmdIndex = -1; |
759 |
/* Index of most recent toplevel command in |
760 |
* the command location table. Initialized |
761 |
* to avoid compiler warning. */ |
762 |
int startCodeOffset = -1; /* Offset of first byte of current command's |
763 |
* code. Init. to avoid compiler warning. */ |
764 |
unsigned char *entryCodeNext = envPtr->codeNext; |
765 |
char *p, *next; |
766 |
Namespace *cmdNsPtr; |
767 |
Command *cmdPtr; |
768 |
Tcl_Token *tokenPtr; |
769 |
int bytesLeft, isFirstCmd, gotParse, wordIdx, currCmdIndex; |
770 |
int commandLength, objIndex, code; |
771 |
char prev; |
772 |
Tcl_DString ds; |
773 |
|
774 |
Tcl_DStringInit(&ds); |
775 |
|
776 |
if (numBytes < 0) { |
777 |
numBytes = strlen(script); |
778 |
} |
779 |
Tcl_ResetResult(interp); |
780 |
isFirstCmd = 1; |
781 |
|
782 |
/* |
783 |
* Each iteration through the following loop compiles the next |
784 |
* command from the script. |
785 |
*/ |
786 |
|
787 |
p = script; |
788 |
bytesLeft = numBytes; |
789 |
gotParse = 0; |
790 |
while (bytesLeft > 0) { |
791 |
if (Tcl_ParseCommand(interp, p, bytesLeft, nested, &parse) != TCL_OK) { |
792 |
code = TCL_ERROR; |
793 |
goto error; |
794 |
} |
795 |
gotParse = 1; |
796 |
if (parse.numWords > 0) { |
797 |
/* |
798 |
* If not the first command, pop the previous command's result |
799 |
* and, if we're compiling a top level command, update the last |
800 |
* command's code size to account for the pop instruction. |
801 |
*/ |
802 |
|
803 |
if (!isFirstCmd) { |
804 |
TclEmitOpcode(INST_POP, envPtr); |
805 |
if (!nested) { |
806 |
envPtr->cmdMapPtr[lastTopLevelCmdIndex].numCodeBytes = |
807 |
(envPtr->codeNext - envPtr->codeStart) |
808 |
- startCodeOffset; |
809 |
} |
810 |
} |
811 |
|
812 |
/* |
813 |
* Determine the actual length of the command. |
814 |
*/ |
815 |
|
816 |
commandLength = parse.commandSize; |
817 |
prev = '\0'; |
818 |
if (commandLength > 0) { |
819 |
prev = parse.commandStart[commandLength-1]; |
820 |
} |
821 |
if (((parse.commandStart+commandLength) != (script+numBytes)) |
822 |
|| ((prev=='\n') || (nested && (prev==']')))) { |
823 |
/* |
824 |
* The command didn't end at the end of the script (i.e. it |
825 |
* ended at a terminator character such as ";". Reduce the |
826 |
* length by one so that the trace message doesn't include |
827 |
* the terminator character. |
828 |
*/ |
829 |
|
830 |
commandLength -= 1; |
831 |
} |
832 |
|
833 |
/* |
834 |
* If tracing, print a line for each top level command compiled. |
835 |
*/ |
836 |
|
837 |
if ((tclTraceCompile >= 1) |
838 |
&& !nested && (envPtr->procPtr == NULL)) { |
839 |
fprintf(stdout, " Compiling: "); |
840 |
TclPrintSource(stdout, parse.commandStart, |
841 |
TclMin(commandLength, 55)); |
842 |
fprintf(stdout, "\n"); |
843 |
} |
844 |
|
845 |
/* |
846 |
* Each iteration of the following loop compiles one word |
847 |
* from the command. |
848 |
*/ |
849 |
|
850 |
envPtr->numCommands++; |
851 |
currCmdIndex = (envPtr->numCommands - 1); |
852 |
if (!nested) { |
853 |
lastTopLevelCmdIndex = currCmdIndex; |
854 |
} |
855 |
startCodeOffset = (envPtr->codeNext - envPtr->codeStart); |
856 |
EnterCmdStartData(envPtr, currCmdIndex, |
857 |
(parse.commandStart - envPtr->source), startCodeOffset); |
858 |
|
859 |
for (wordIdx = 0, tokenPtr = parse.tokenPtr; |
860 |
wordIdx < parse.numWords; |
861 |
wordIdx++, tokenPtr += (tokenPtr->numComponents + 1)) { |
862 |
if (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD) { |
863 |
/* |
864 |
* If this is the first word and the command has a |
865 |
* compile procedure, let it compile the command. |
866 |
*/ |
867 |
|
868 |
if (wordIdx == 0) { |
869 |
if (envPtr->procPtr != NULL) { |
870 |
cmdNsPtr = envPtr->procPtr->cmdPtr->nsPtr; |
871 |
} else { |
872 |
cmdNsPtr = NULL; /* use current NS */ |
873 |
} |
874 |
|
875 |
/* |
876 |
* We copy the string before trying to find the command |
877 |
* by name. We used to modify the string in place, but |
878 |
* this is not safe because the name resolution |
879 |
* handlers could have side effects that rely on the |
880 |
* unmodified string. |
881 |
*/ |
882 |
|
883 |
Tcl_DStringSetLength(&ds, 0); |
884 |
Tcl_DStringAppend(&ds, tokenPtr[1].start, |
885 |
tokenPtr[1].size); |
886 |
|
887 |
cmdPtr = (Command *) Tcl_FindCommand(interp, |
888 |
Tcl_DStringValue(&ds), |
889 |
(Tcl_Namespace *) cmdNsPtr, /*flags*/ 0); |
890 |
|
891 |
if ((cmdPtr != NULL) |
892 |
&& (cmdPtr->compileProc != NULL) |
893 |
&& !(iPtr->flags & DONT_COMPILE_CMDS_INLINE)) { |
894 |
code = (*(cmdPtr->compileProc))(interp, &parse, |
895 |
envPtr); |
896 |
if (code == TCL_OK) { |
897 |
maxDepth = TclMax(envPtr->maxStackDepth, |
898 |
maxDepth); |
899 |
goto finishCommand; |
900 |
} else if (code == TCL_OUT_LINE_COMPILE) { |
901 |
/* do nothing */ |
902 |
} else { /* an error */ |
903 |
/* |
904 |
* There was a compilation error, the last |
905 |
* command did not get compiled into (*envPtr). |
906 |
* Decrement the number of commands |
907 |
* claimed to be in (*envPtr). |
908 |
*/ |
909 |
envPtr->numCommands--; |
910 |
goto error; |
911 |
} |
912 |
} |
913 |
|
914 |
/* |
915 |
* No compile procedure so push the word. If the |
916 |
* command was found, push a CmdName object to |
917 |
* reduce runtime lookups. |
918 |
*/ |
919 |
|
920 |
objIndex = TclRegisterLiteral(envPtr, |
921 |
tokenPtr[1].start, tokenPtr[1].size, |
922 |
/*onHeap*/ 0); |
923 |
if (cmdPtr != NULL) { |
924 |
TclSetCmdNameObj(interp, |
925 |
envPtr->literalArrayPtr[objIndex].objPtr, |
926 |
cmdPtr); |
927 |
} |
928 |
} else { |
929 |
objIndex = TclRegisterLiteral(envPtr, |
930 |
tokenPtr[1].start, tokenPtr[1].size, |
931 |
/*onHeap*/ 0); |
932 |
} |
933 |
TclEmitPush(objIndex, envPtr); |
934 |
maxDepth = TclMax((wordIdx + 1), maxDepth); |
935 |
} else { |
936 |
/* |
937 |
* The word is not a simple string of characters. |
938 |
*/ |
939 |
|
940 |
code = TclCompileTokens(interp, tokenPtr+1, |
941 |
tokenPtr->numComponents, envPtr); |
942 |
if (code != TCL_OK) { |
943 |
goto error; |
944 |
} |
945 |
maxDepth = TclMax((wordIdx + envPtr->maxStackDepth), |
946 |
maxDepth); |
947 |
} |
948 |
} |
949 |
|
950 |
/* |
951 |
* Emit an invoke instruction for the command. We skip this |
952 |
* if a compile procedure was found for the command. |
953 |
*/ |
954 |
|
955 |
if (wordIdx > 0) { |
956 |
if (wordIdx <= 255) { |
957 |
TclEmitInstInt1(INST_INVOKE_STK1, wordIdx, envPtr); |
958 |
} else { |
959 |
TclEmitInstInt4(INST_INVOKE_STK4, wordIdx, envPtr); |
960 |
} |
961 |
} |
962 |
|
963 |
/* |
964 |
* Update the compilation environment structure and record the |
965 |
* offsets of the source and code for the command. |
966 |
*/ |
967 |
|
968 |
finishCommand: |
969 |
EnterCmdExtentData(envPtr, currCmdIndex, commandLength, |
970 |
(envPtr->codeNext-envPtr->codeStart) - startCodeOffset); |
971 |
isFirstCmd = 0; |
972 |
} /* end if parse.numWords > 0 */ |
973 |
|
974 |
/* |
975 |
* Advance to the next command in the script. |
976 |
*/ |
977 |
|
978 |
next = parse.commandStart + parse.commandSize; |
979 |
bytesLeft -= (next - p); |
980 |
p = next; |
981 |
Tcl_FreeParse(&parse); |
982 |
gotParse = 0; |
983 |
if (nested && (p[-1] == ']')) { |
984 |
/* |
985 |
* We get here in the special case where TCL_BRACKET_TERM was |
986 |
* set in the interpreter and we reached a close bracket in the |
987 |
* script. Stop compilation. |
988 |
*/ |
989 |
|
990 |
break; |
991 |
} |
992 |
} |
993 |
|
994 |
/* |
995 |
* If the source script yielded no instructions (e.g., if it was empty), |
996 |
* push an empty string as the command's result. |
997 |
*/ |
998 |
|
999 |
if (envPtr->codeNext == entryCodeNext) { |
1000 |
TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0), |
1001 |
envPtr); |
1002 |
maxDepth = 1; |
1003 |
} |
1004 |
|
1005 |
if ((nested != 0) && (p > script) && (p[-1] == ']')) { |
1006 |
iPtr->termOffset = (p - 1) - script; |
1007 |
} else { |
1008 |
iPtr->termOffset = (p - script); |
1009 |
} |
1010 |
envPtr->maxStackDepth = maxDepth; |
1011 |
Tcl_DStringFree(&ds); |
1012 |
return TCL_OK; |
1013 |
|
1014 |
error: |
1015 |
/* |
1016 |
* Generate various pieces of error information, such as the line |
1017 |
* number where the error occurred and information to add to the |
1018 |
* errorInfo variable. Then free resources that had been allocated |
1019 |
* to the command. |
1020 |
*/ |
1021 |
|
1022 |
commandLength = parse.commandSize; |
1023 |
prev = '\0'; |
1024 |
if (commandLength > 0) { |
1025 |
prev = parse.commandStart[commandLength-1]; |
1026 |
} |
1027 |
if (((parse.commandStart+commandLength) != (script+numBytes)) |
1028 |
|| ((prev == '\n') || (nested && (prev == ']')))) { |
1029 |
/* |
1030 |
* The command where the error occurred didn't end at the end |
1031 |
* of the script (i.e. it ended at a terminator character such |
1032 |
* as ";". Reduce the length by one so that the error message |
1033 |
* doesn't include the terminator character. |
1034 |
*/ |
1035 |
|
1036 |
commandLength -= 1; |
1037 |
} |
1038 |
LogCompilationInfo(interp, script, parse.commandStart, commandLength); |
1039 |
if (gotParse) { |
1040 |
Tcl_FreeParse(&parse); |
1041 |
} |
1042 |
iPtr->termOffset = (p - script); |
1043 |
envPtr->maxStackDepth = maxDepth; |
1044 |
Tcl_DStringFree(&ds); |
1045 |
return code; |
1046 |
} |
1047 |
|
1048 |
/* |
1049 |
*---------------------------------------------------------------------- |
1050 |
* |
1051 |
* TclCompileTokens -- |
1052 |
* |
1053 |
* Given an array of tokens parsed from a Tcl command (e.g., the tokens |
1054 |
* that make up a word) this procedure emits instructions to evaluate |
1055 |
* the tokens and concatenate their values to form a single result |
1056 |
* value on the interpreter's runtime evaluation stack. |
1057 |
* |
1058 |
* Results: |
1059 |
* The return value is a standard Tcl result. If an error occurs, an |
1060 |
* error message is left in the interpreter's result. |
1061 |
* |
1062 |
* envPtr->maxStackDepth is updated with the maximum number of stack |
1063 |
* elements needed to evaluate the tokens. |
1064 |
* |
1065 |
* Side effects: |
1066 |
* Instructions are added to envPtr to push and evaluate the tokens |
1067 |
* at runtime. |
1068 |
* |
1069 |
*---------------------------------------------------------------------- |
1070 |
*/ |
1071 |
|
1072 |
int |
1073 |
TclCompileTokens(interp, tokenPtr, count, envPtr) |
1074 |
Tcl_Interp *interp; /* Used for error and status reporting. */ |
1075 |
Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens |
1076 |
* to compile. */ |
1077 |
int count; /* Number of tokens to consider at tokenPtr. |
1078 |
* Must be at least 1. */ |
1079 |
CompileEnv *envPtr; /* Holds the resulting instructions. */ |
1080 |
{ |
1081 |
Tcl_DString textBuffer; /* Holds concatenated chars from adjacent |
1082 |
* TCL_TOKEN_TEXT, TCL_TOKEN_BS tokens. */ |
1083 |
char buffer[TCL_UTF_MAX]; |
1084 |
char *name, *p; |
1085 |
int numObjsToConcat, nameBytes, hasNsQualifiers, localVar; |
1086 |
int length, maxDepth, depthForVar, i, code; |
1087 |
unsigned char *entryCodeNext = envPtr->codeNext; |
1088 |
|
1089 |
Tcl_DStringInit(&textBuffer); |
1090 |
maxDepth = 0; |
1091 |
numObjsToConcat = 0; |
1092 |
for ( ; count > 0; count--, tokenPtr++) { |
1093 |
switch (tokenPtr->type) { |
1094 |
case TCL_TOKEN_TEXT: |
1095 |
Tcl_DStringAppend(&textBuffer, tokenPtr->start, |
1096 |
tokenPtr->size); |
1097 |
break; |
1098 |
|
1099 |
case TCL_TOKEN_BS: |
1100 |
length = Tcl_UtfBackslash(tokenPtr->start, (int *) NULL, |
1101 |
buffer); |
1102 |
Tcl_DStringAppend(&textBuffer, buffer, length); |
1103 |
break; |
1104 |
|
1105 |
case TCL_TOKEN_COMMAND: |
1106 |
/* |
1107 |
* Push any accumulated chars appearing before the command. |
1108 |
*/ |
1109 |
|
1110 |
if (Tcl_DStringLength(&textBuffer) > 0) { |
1111 |
int literal; |
1112 |
|
1113 |
literal = TclRegisterLiteral(envPtr, |
1114 |
Tcl_DStringValue(&textBuffer), |
1115 |
Tcl_DStringLength(&textBuffer), /*onHeap*/ 0); |
1116 |
TclEmitPush(literal, envPtr); |
1117 |
numObjsToConcat++; |
1118 |
maxDepth = TclMax(numObjsToConcat, maxDepth); |
1119 |
Tcl_DStringFree(&textBuffer); |
1120 |
} |
1121 |
|
1122 |
code = TclCompileScript(interp, tokenPtr->start+1, |
1123 |
tokenPtr->size-2, /*nested*/ 1, envPtr); |
1124 |
if (code != TCL_OK) { |
1125 |
goto error; |
1126 |
} |
1127 |
maxDepth = TclMax((numObjsToConcat + envPtr->maxStackDepth), |
1128 |
maxDepth); |
1129 |
numObjsToConcat++; |
1130 |
break; |
1131 |
|
1132 |
case TCL_TOKEN_VARIABLE: |
1133 |
/* |
1134 |
* Push any accumulated chars appearing before the $<var>. |
1135 |
*/ |
1136 |
|
1137 |
if (Tcl_DStringLength(&textBuffer) > 0) { |
1138 |
int literal; |
1139 |
|
1140 |
literal = TclRegisterLiteral(envPtr, |
1141 |
Tcl_DStringValue(&textBuffer), |
1142 |
Tcl_DStringLength(&textBuffer), /*onHeap*/ 0); |
1143 |
TclEmitPush(literal, envPtr); |
1144 |
numObjsToConcat++; |
1145 |
maxDepth = TclMax(numObjsToConcat, maxDepth); |
1146 |
Tcl_DStringFree(&textBuffer); |
1147 |
} |
1148 |
|
1149 |
/* |
1150 |
* Check if the name contains any namespace qualifiers. |
1151 |
*/ |
1152 |
|
1153 |
name = tokenPtr[1].start; |
1154 |
nameBytes = tokenPtr[1].size; |
1155 |
hasNsQualifiers = 0; |
1156 |
for (i = 0, p = name; i < nameBytes; i++, p++) { |
1157 |
if ((*p == ':') && (i < (nameBytes-1)) |
1158 |
&& (*(p+1) == ':')) { |
1159 |
hasNsQualifiers = 1; |
1160 |
break; |
1161 |
} |
1162 |
} |
1163 |
|
1164 |
/* |
1165 |
* Either push the variable's name, or find its index in |
1166 |
* the array of local variables in a procedure frame. |
1167 |
*/ |
1168 |
|
1169 |
depthForVar = 0; |
1170 |
if ((envPtr->procPtr == NULL) || hasNsQualifiers) { |
1171 |
localVar = -1; |
1172 |
TclEmitPush(TclRegisterLiteral(envPtr, name, nameBytes, |
1173 |
/*onHeap*/ 0), envPtr); |
1174 |
depthForVar = 1; |
1175 |
} else { |
1176 |
localVar = TclFindCompiledLocal(name, nameBytes, |
1177 |
/*create*/ 0, /*flags*/ 0, envPtr->procPtr); |
1178 |
if (localVar < 0) { |
1179 |
TclEmitPush(TclRegisterLiteral(envPtr, name, |
1180 |
nameBytes, /*onHeap*/ 0), envPtr); |
1181 |
depthForVar = 1; |
1182 |
} |
1183 |
} |
1184 |
|
1185 |
/* |
1186 |
* Emit instructions to load the variable. |
1187 |
*/ |
1188 |
|
1189 |
if (tokenPtr->numComponents == 1) { |
1190 |
if (localVar < 0) { |
1191 |
TclEmitOpcode(INST_LOAD_SCALAR_STK, envPtr); |
1192 |
} else if (localVar <= 255) { |
1193 |
TclEmitInstInt1(INST_LOAD_SCALAR1, localVar, |
1194 |
envPtr); |
1195 |
} else { |
1196 |
TclEmitInstInt4(INST_LOAD_SCALAR4, localVar, |
1197 |
envPtr); |
1198 |
} |
1199 |
} else { |
1200 |
code = TclCompileTokens(interp, tokenPtr+2, |
1201 |
tokenPtr->numComponents-1, envPtr); |
1202 |
if (code != TCL_OK) { |
1203 |
sprintf(buffer, |
1204 |
"\n (parsing index for array \"%.*s\")", |
1205 |
((nameBytes > 100)? 100 : nameBytes), name); |
1206 |
Tcl_AddObjErrorInfo(interp, buffer, -1); |
1207 |
goto error; |
1208 |
} |
1209 |
depthForVar += envPtr->maxStackDepth; |
1210 |
if (localVar < 0) { |
1211 |
TclEmitOpcode(INST_LOAD_ARRAY_STK, envPtr); |
1212 |
} else if (localVar <= 255) { |
1213 |
TclEmitInstInt1(INST_LOAD_ARRAY1, localVar, |
1214 |
envPtr); |
1215 |
} else { |
1216 |
TclEmitInstInt4(INST_LOAD_ARRAY4, localVar, |
1217 |
envPtr); |
1218 |
} |
1219 |
} |
1220 |
maxDepth = TclMax(numObjsToConcat + depthForVar, maxDepth); |
1221 |
numObjsToConcat++; |
1222 |
count -= tokenPtr->numComponents; |
1223 |
tokenPtr += tokenPtr->numComponents; |
1224 |
break; |
1225 |
|
1226 |
default: |
1227 |
panic("Unexpected token type in TclCompileTokens"); |
1228 |
} |
1229 |
} |
1230 |
|
1231 |
/* |
1232 |
* Push any accumulated characters appearing at the end. |
1233 |
*/ |
1234 |
|
1235 |
if (Tcl_DStringLength(&textBuffer) > 0) { |
1236 |
int literal; |
1237 |
|
1238 |
literal = TclRegisterLiteral(envPtr, Tcl_DStringValue(&textBuffer), |
1239 |
Tcl_DStringLength(&textBuffer), /*onHeap*/ 0); |
1240 |
TclEmitPush(literal, envPtr); |
1241 |
numObjsToConcat++; |
1242 |
maxDepth = TclMax(numObjsToConcat, maxDepth); |
1243 |
} |
1244 |
|
1245 |
/* |
1246 |
* If necessary, concatenate the parts of the word. |
1247 |
*/ |
1248 |
|
1249 |
while (numObjsToConcat > 255) { |
1250 |
TclEmitInstInt1(INST_CONCAT1, 255, envPtr); |
1251 |
numObjsToConcat -= 254; /* concat pushes 1 obj, the result */ |
1252 |
} |
1253 |
if (numObjsToConcat > 1) { |
1254 |
TclEmitInstInt1(INST_CONCAT1, numObjsToConcat, envPtr); |
1255 |
} |
1256 |
|
1257 |
/* |
1258 |
* If the tokens yielded no instructions, push an empty string. |
1259 |
*/ |
1260 |
|
1261 |
if (envPtr->codeNext == entryCodeNext) { |
1262 |
TclEmitPush(TclRegisterLiteral(envPtr, "", 0, /*onHeap*/ 0), |
1263 |
envPtr); |
1264 |
maxDepth = 1; |
1265 |
} |
1266 |
Tcl_DStringFree(&textBuffer); |
1267 |
envPtr->maxStackDepth = maxDepth; |
1268 |
return TCL_OK; |
1269 |
|
1270 |
error: |
1271 |
Tcl_DStringFree(&textBuffer); |
1272 |
envPtr->maxStackDepth = maxDepth; |
1273 |
return code; |
1274 |
} |
1275 |
|
1276 |
/* |
1277 |
*---------------------------------------------------------------------- |
1278 |
* |
1279 |
* TclCompileCmdWord -- |
1280 |
* |
1281 |
* Given an array of parse tokens for a word containing one or more Tcl |
1282 |
* commands, emit inline instructions to execute them. This procedure |
1283 |
* differs from TclCompileTokens in that a simple word such as a loop |
1284 |
* body enclosed in braces is not just pushed as a string, but is |
1285 |
* itself parsed into tokens and compiled. |
1286 |
* |
1287 |
* Results: |
1288 |
* The return value is a standard Tcl result. If an error occurs, an |
1289 |
* error message is left in the interpreter's result. |
1290 |
* |
1291 |
* envPtr->maxStackDepth is updated with the maximum number of stack |
1292 |
* elements needed to execute the tokens. |
1293 |
* |
1294 |
* Side effects: |
1295 |
* Instructions are added to envPtr to execute the tokens at runtime. |
1296 |
* |
1297 |
*---------------------------------------------------------------------- |
1298 |
*/ |
1299 |
|
1300 |
int |
1301 |
TclCompileCmdWord(interp, tokenPtr, count, envPtr) |
1302 |
Tcl_Interp *interp; /* Used for error and status reporting. */ |
1303 |
Tcl_Token *tokenPtr; /* Pointer to first in an array of tokens |
1304 |
* for a command word to compile inline. */ |
1305 |
int count; /* Number of tokens to consider at tokenPtr. |
1306 |
* Must be at least 1. */ |
1307 |
CompileEnv *envPtr; /* Holds the resulting instructions. */ |
1308 |
{ |
1309 |
int code; |
1310 |
|
1311 |
/* |
1312 |
* Handle the common case: if there is a single text token, compile it |
1313 |
* into an inline sequence of instructions. |
1314 |
*/ |
1315 |
|
1316 |
envPtr->maxStackDepth = 0; |
1317 |
if ((count == 1) && (tokenPtr->type == TCL_TOKEN_TEXT)) { |
1318 |
code = TclCompileScript(interp, tokenPtr->start, tokenPtr->size, |
1319 |
/*nested*/ 0, envPtr); |
1320 |
return code; |
1321 |
} |
1322 |
|
1323 |
/* |
1324 |
* Multiple tokens or the single token involves substitutions. Emit |
1325 |
* instructions to invoke the eval command procedure at runtime on the |
1326 |
* result of evaluating the tokens. |
1327 |
*/ |
1328 |
|
1329 |
code = TclCompileTokens(interp, tokenPtr, count, envPtr); |
1330 |
if (code != TCL_OK) { |
1331 |
return code; |
1332 |
} |
1333 |
TclEmitOpcode(INST_EVAL_STK, envPtr); |
1334 |
return TCL_OK; |
1335 |
} |
1336 |
|
1337 |
/* |
1338 |
*---------------------------------------------------------------------- |
1339 |
* |
1340 |
* TclCompileExprWords -- |
1341 |
* |
1342 |
* Given an array of parse tokens representing one or more words that |
1343 |
* contain a Tcl expression, emit inline instructions to execute the |
1344 |
* expression. This procedure differs from TclCompileExpr in that it |
1345 |
* supports Tcl's two-level substitution semantics for expressions that |
1346 |
* appear as command words. |
1347 |
* |
1348 |
* Results: |
1349 |
* The return value is a standard Tcl result. If an error occurs, an |
1350 |
* error message is left in the interpreter's result. |
1351 |
* |
1352 |
* envPtr->maxStackDepth is updated with the maximum number of stack |
1353 |
* elements needed to execute the expression. |
1354 |
* |
1355 |
* Side effects: |
1356 |
* Instructions are added to envPtr to execute the expression. |
1357 |
* |
1358 |
*---------------------------------------------------------------------- |
1359 |
*/ |
1360 |
|
1361 |
int |
1362 |
TclCompileExprWords(interp, tokenPtr, numWords, envPtr) |
1363 |
Tcl_Interp *interp; /* Used for error and status reporting. */ |
1364 |
Tcl_Token *tokenPtr; /* Points to first in an array of word |
1365 |
* tokens tokens for the expression to |
1366 |
* compile inline. */ |
1367 |
int numWords; /* Number of word tokens starting at |
1368 |
* tokenPtr. Must be at least 1. Each word |
1369 |
* token contains one or more subtokens. */ |
1370 |
CompileEnv *envPtr; /* Holds the resulting instructions. */ |
1371 |
{ |
1372 |
Tcl_Token *wordPtr; |
1373 |
int maxDepth, range, numBytes, i, code; |
1374 |
char *script; |
1375 |
int saveExprIsJustVarRef = envPtr->exprIsJustVarRef; |
1376 |
int saveExprIsComparison = envPtr->exprIsComparison; |
1377 |
|
1378 |
envPtr->maxStackDepth = 0; |
1379 |
maxDepth = 0; |
1380 |
range = -1; |
1381 |
code = TCL_OK; |
1382 |
|
1383 |
/* |
1384 |
* If the expression is a single word that doesn't require |
1385 |
* substitutions, just compile it's string into inline instructions. |
1386 |
*/ |
1387 |
|
1388 |
if ((numWords == 1) && (tokenPtr->type == TCL_TOKEN_SIMPLE_WORD)) { |
1389 |
/* |
1390 |
* Temporarily overwrite the character just after the end of the |
1391 |
* string with a 0 byte. |
1392 |
*/ |
1393 |
|
1394 |
script = tokenPtr[1].start; |
1395 |
numBytes = tokenPtr[1].size; |
1396 |
code = TclCompileExpr(interp, script, numBytes, envPtr); |
1397 |
return code; |
1398 |
} |
1399 |
|
1400 |
/* |
1401 |
* Emit code to call the expr command proc at runtime. Concatenate the |
1402 |
* (already substituted once) expr tokens with a space between each. |
1403 |
*/ |
1404 |
|
1405 |
wordPtr = tokenPtr; |
1406 |
for (i = 0; i < numWords; i++) { |
1407 |
code = TclCompileTokens(interp, wordPtr+1, wordPtr->numComponents, |
1408 |
envPtr); |
1409 |
if (code != TCL_OK) { |
1410 |
break; |
1411 |
} |
1412 |
if (i < (numWords - 1)) { |
1413 |
TclEmitPush(TclRegisterLiteral(envPtr, " ", 1, /*onHeap*/ 0), |
1414 |
envPtr); |
1415 |
maxDepth = TclMax((envPtr->maxStackDepth + 1), maxDepth); |
1416 |
} else { |
1417 |
maxDepth = TclMax(envPtr->maxStackDepth, maxDepth); |
1418 |
} |
1419 |
wordPtr += (wordPtr->numComponents + 1); |
1420 |
} |
1421 |
if (code == TCL_OK) { |
1422 |
int concatItems = 2*numWords - 1; |
1423 |
while (concatItems > 255) { |
1424 |
TclEmitInstInt1(INST_CONCAT1, 255, envPtr); |
1425 |
concatItems -= 254; |
1426 |
} |
1427 |
if (concatItems > 1) { |
1428 |
TclEmitInstInt1(INST_CONCAT1, concatItems, envPtr); |
1429 |
} |
1430 |
TclEmitOpcode(INST_EXPR_STK, envPtr); |
1431 |
} |
1432 |
|
1433 |
envPtr->exprIsJustVarRef = saveExprIsJustVarRef; |
1434 |
envPtr->exprIsComparison = saveExprIsComparison; |
1435 |
envPtr->maxStackDepth = maxDepth; |
1436 |
return code; |
1437 |
} |
1438 |
|
1439 |
/* |
1440 |
*---------------------------------------------------------------------- |
1441 |
* |
1442 |
* TclInitByteCodeObj -- |
1443 |
* |
1444 |
* Create a ByteCode structure and initialize it from a CompileEnv |
1445 |
* compilation environment structure. The ByteCode structure is |
1446 |
* smaller and contains just that information needed to execute |
1447 |
* the bytecode instructions resulting from compiling a Tcl script. |
1448 |
* The resulting structure is placed in the specified object. |
1449 |
* |
1450 |
* Results: |
1451 |
* A newly constructed ByteCode object is stored in the internal |
1452 |
* representation of the objPtr. |
1453 |
* |
1454 |
* Side effects: |
1455 |
* A single heap object is allocated to hold the new ByteCode structure |
1456 |
* and its code, object, command location, and aux data arrays. Note |
1457 |
* that "ownership" (i.e., the pointers to) the Tcl objects and aux |
1458 |
* data items will be handed over to the new ByteCode structure from |
1459 |
* the CompileEnv structure. |
1460 |
* |
1461 |
*---------------------------------------------------------------------- |
1462 |
*/ |
1463 |
|
1464 |
void |
1465 |
TclInitByteCodeObj(objPtr, envPtr) |
1466 |
Tcl_Obj *objPtr; /* Points object that should be |
1467 |
* initialized, and whose string rep |
1468 |
* contains the source code. */ |
1469 |
register CompileEnv *envPtr; /* Points to the CompileEnv structure from |
1470 |
* which to create a ByteCode structure. */ |
1471 |
{ |
1472 |
register ByteCode *codePtr; |
1473 |
size_t codeBytes, objArrayBytes, exceptArrayBytes, cmdLocBytes; |
1474 |
size_t auxDataArrayBytes, structureSize; |
1475 |
register unsigned char *p; |
1476 |
unsigned char *nextPtr; |
1477 |
int numLitObjects = envPtr->literalArrayNext; |
1478 |
Namespace *namespacePtr; |
1479 |
int i; |
1480 |
Interp *iPtr; |
1481 |
|
1482 |
iPtr = envPtr->iPtr; |
1483 |
|
1484 |
codeBytes = (envPtr->codeNext - envPtr->codeStart); |
1485 |
objArrayBytes = (envPtr->literalArrayNext * sizeof(Tcl_Obj *)); |
1486 |
exceptArrayBytes = (envPtr->exceptArrayNext * sizeof(ExceptionRange)); |
1487 |
auxDataArrayBytes = (envPtr->auxDataArrayNext * sizeof(AuxData)); |
1488 |
cmdLocBytes = GetCmdLocEncodingSize(envPtr); |
1489 |
|
1490 |
/* |
1491 |
* Compute the total number of bytes needed for this bytecode. |
1492 |
*/ |
1493 |
|
1494 |
structureSize = sizeof(ByteCode); |
1495 |
structureSize += TCL_ALIGN(codeBytes); /* align object array */ |
1496 |
structureSize += TCL_ALIGN(objArrayBytes); /* align exc range arr */ |
1497 |
structureSize += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */ |
1498 |
structureSize += auxDataArrayBytes; |
1499 |
structureSize += cmdLocBytes; |
1500 |
|
1501 |
if (envPtr->iPtr->varFramePtr != NULL) { |
1502 |
namespacePtr = envPtr->iPtr->varFramePtr->nsPtr; |
1503 |
} else { |
1504 |
namespacePtr = envPtr->iPtr->globalNsPtr; |
1505 |
} |
1506 |
|
1507 |
p = (unsigned char *) ckalloc((size_t) structureSize); |
1508 |
codePtr = (ByteCode *) p; |
1509 |
codePtr->interpHandle = TclHandlePreserve(iPtr->handle); |
1510 |
codePtr->compileEpoch = iPtr->compileEpoch; |
1511 |
codePtr->nsPtr = namespacePtr; |
1512 |
codePtr->nsEpoch = namespacePtr->resolverEpoch; |
1513 |
codePtr->refCount = 1; |
1514 |
codePtr->flags = 0; |
1515 |
codePtr->source = envPtr->source; |
1516 |
codePtr->procPtr = envPtr->procPtr; |
1517 |
|
1518 |
codePtr->numCommands = envPtr->numCommands; |
1519 |
codePtr->numSrcBytes = envPtr->numSrcBytes; |
1520 |
codePtr->numCodeBytes = codeBytes; |
1521 |
codePtr->numLitObjects = numLitObjects; |
1522 |
codePtr->numExceptRanges = envPtr->exceptArrayNext; |
1523 |
codePtr->numAuxDataItems = envPtr->auxDataArrayNext; |
1524 |
codePtr->numCmdLocBytes = cmdLocBytes; |
1525 |
codePtr->maxExceptDepth = envPtr->maxExceptDepth; |
1526 |
codePtr->maxStackDepth = envPtr->maxStackDepth; |
1527 |
|
1528 |
p += sizeof(ByteCode); |
1529 |
codePtr->codeStart = p; |
1530 |
memcpy((VOID *) p, (VOID *) envPtr->codeStart, (size_t) codeBytes); |
1531 |
|
1532 |
p += TCL_ALIGN(codeBytes); /* align object array */ |
1533 |
codePtr->objArrayPtr = (Tcl_Obj **) p; |
1534 |
for (i = 0; i < numLitObjects; i++) { |
1535 |
codePtr->objArrayPtr[i] = envPtr->literalArrayPtr[i].objPtr; |
1536 |
} |
1537 |
|
1538 |
p += TCL_ALIGN(objArrayBytes); /* align exception range array */ |
1539 |
if (exceptArrayBytes > 0) { |
1540 |
codePtr->exceptArrayPtr = (ExceptionRange *) p; |
1541 |
memcpy((VOID *) p, (VOID *) envPtr->exceptArrayPtr, |
1542 |
(size_t) exceptArrayBytes); |
1543 |
} else { |
1544 |
codePtr->exceptArrayPtr = NULL; |
1545 |
} |
1546 |
|
1547 |
p += TCL_ALIGN(exceptArrayBytes); /* align AuxData array */ |
1548 |
if (auxDataArrayBytes > 0) { |
1549 |
codePtr->auxDataArrayPtr = (AuxData *) p; |
1550 |
memcpy((VOID *) p, (VOID *) envPtr->auxDataArrayPtr, |
1551 |
(size_t) auxDataArrayBytes); |
1552 |
} else { |
1553 |
codePtr->auxDataArrayPtr = NULL; |
1554 |
} |
1555 |
|
1556 |
p += auxDataArrayBytes; |
1557 |
nextPtr = EncodeCmdLocMap(envPtr, codePtr, (unsigned char *) p); |
1558 |
#ifdef TCL_COMPILE_DEBUG |
1559 |
if (((size_t)(nextPtr - p)) != cmdLocBytes) { |
1560 |
panic("TclInitByteCodeObj: encoded cmd location bytes %d != expected size %d\n", (nextPtr - p), cmdLocBytes); |
1561 |
} |
1562 |
#endif |
1563 |
|
1564 |
/* |
1565 |
* Record various compilation-related statistics about the new ByteCode |
1566 |
* structure. Don't include overhead for statistics-related fields. |
1567 |
*/ |
1568 |
|
1569 |
#ifdef TCL_COMPILE_STATS |
1570 |
codePtr->structureSize = structureSize |
1571 |
- (sizeof(size_t) + sizeof(Tcl_Time)); |
1572 |
TclpGetTime(&(codePtr->createTime)); |
1573 |
|
1574 |
RecordByteCodeStats(codePtr); |
1575 |
#endif /* TCL_COMPILE_STATS */ |
1576 |
|
1577 |
/* |
1578 |
* Free the old internal rep then convert the object to a |
1579 |
* bytecode object by making its internal rep point to the just |
1580 |
* compiled ByteCode. |
1581 |
*/ |
1582 |
|
1583 |
if ((objPtr->typePtr != NULL) && |
1584 |
(objPtr->typePtr->freeIntRepProc != NULL)) { |
1585 |
(*objPtr->typePtr->freeIntRepProc)(objPtr); |
1586 |
} |
1587 |
objPtr->internalRep.otherValuePtr = (VOID *) codePtr; |
1588 |
objPtr->typePtr = &tclByteCodeType; |
1589 |
} |
1590 |
|
1591 |
/* |
1592 |
*---------------------------------------------------------------------- |
1593 |
* |
1594 |
* LogCompilationInfo -- |
1595 |
* |
1596 |
* This procedure is invoked after an error occurs during compilation. |
1597 |
* It adds information to the "errorInfo" variable to describe the |
1598 |
* command that was being compiled when the error occurred. |
1599 |
* |
1600 |
* Results: |
1601 |
* None. |
1602 |
* |
1603 |
* Side effects: |
1604 |
* Information about the command is added to errorInfo and the |
1605 |
* line number stored internally in the interpreter is set. If this |
1606 |
* is the first call to this procedure or Tcl_AddObjErrorInfo since |
1607 |
* an error occurred, then old information in errorInfo is |
1608 |
* deleted. |
1609 |
* |
1610 |
*---------------------------------------------------------------------- |
1611 |
*/ |
1612 |
|
1613 |
static void |
1614 |
LogCompilationInfo(interp, script, command, length) |
1615 |
Tcl_Interp *interp; /* Interpreter in which to log the |
1616 |
* information. */ |
1617 |
char *script; /* First character in script containing |
1618 |
* command (must be <= command). */ |
1619 |
char *command; /* First character in command that |
1620 |
* generated the error. */ |
1621 |
int length; /* Number of bytes in command (-1 means |
1622 |
* use all bytes up to first null byte). */ |
1623 |
{ |
1624 |
char buffer[200]; |
1625 |
register char *p; |
1626 |
char *ellipsis = ""; |
1627 |
Interp *iPtr = (Interp *) interp; |
1628 |
|
1629 |
if (iPtr->flags & ERR_ALREADY_LOGGED) { |
1630 |
/* |
1631 |
* Someone else has already logged error information for this |
1632 |
* command; we shouldn't add anything more. |
1633 |
*/ |
1634 |
|
1635 |
return; |
1636 |
} |
1637 |
|
1638 |
/* |
1639 |
* Compute the line number where the error occurred. |
1640 |
*/ |
1641 |
|
1642 |
iPtr->errorLine = 1; |
1643 |
for (p = script; p != command; p++) { |
1644 |
if (*p == '\n') { |
1645 |
iPtr->errorLine++; |
1646 |
} |
1647 |
} |
1648 |
|
1649 |
/* |
1650 |
* Create an error message to add to errorInfo, including up to a |
1651 |
* maximum number of characters of the command. |
1652 |
*/ |
1653 |
|
1654 |
if (length < 0) { |
1655 |
length = strlen(command); |
1656 |
} |
1657 |
if (length > 150) { |
1658 |
length = 150; |
1659 |
ellipsis = "..."; |
1660 |
} |
1661 |
sprintf(buffer, "\n while compiling\n\"%.*s%s\"", |
1662 |
length, command, ellipsis); |
1663 |
Tcl_AddObjErrorInfo(interp, buffer, -1); |
1664 |
} |
1665 |
|
1666 |
/* |
1667 |
*---------------------------------------------------------------------- |
1668 |
* |
1669 |
* TclFindCompiledLocal -- |
1670 |
* |
1671 |
* This procedure is called at compile time to look up and optionally |
1672 |
* allocate an entry ("slot") for a variable in a procedure's array of |
1673 |
* local variables. If the variable's name is NULL, a new temporary |
1674 |
* variable is always created. (Such temporary variables can only be |
1675 |
* referenced using their slot index.) |
1676 |
* |
1677 |
* Results: |
1678 |
* If create is 0 and the name is non-NULL, then if the variable is |
1679 |
* found, the index of its entry in the procedure's array of local |
1680 |
* variables is returned; otherwise -1 is returned. If name is NULL, |
1681 |
* the index of a new temporary variable is returned. Finally, if |
1682 |
* create is 1 and name is non-NULL, the index of a new entry is |
1683 |
* returned. |
1684 |
* |
1685 |
* Side effects: |
1686 |
* Creates and registers a new local variable if create is 1 and |
1687 |
* the variable is unknown, or if the name is NULL. |
1688 |
* |
1689 |
*---------------------------------------------------------------------- |
1690 |
*/ |
1691 |
|
1692 |
int |
1693 |
TclFindCompiledLocal(name, nameBytes, create, flags, procPtr) |
1694 |
register char *name; /* Points to first character of the name of |
1695 |
* a scalar or array variable. If NULL, a |
1696 |
* temporary var should be created. */ |
1697 |
int nameBytes; /* Number of bytes in the name. */ |
1698 |
int create; /* If 1, allocate a local frame entry for |
1699 |
* the variable if it is new. */ |
1700 |
int flags; /* Flag bits for the compiled local if |
1701 |
* created. Only VAR_SCALAR, VAR_ARRAY, and |
1702 |
* VAR_LINK make sense. */ |
1703 |
register Proc *procPtr; /* Points to structure describing procedure |
1704 |
* containing the variable reference. */ |
1705 |
{ |
1706 |
register CompiledLocal *localPtr; |
1707 |
int localVar = -1; |
1708 |
register int i; |
1709 |
|
1710 |
/* |
1711 |
* If not creating a temporary, does a local variable of the specified |
1712 |
* name already exist? |
1713 |
*/ |
1714 |
|
1715 |
if (name != NULL) { |
1716 |
int localCt = procPtr->numCompiledLocals; |
1717 |
localPtr = procPtr->firstLocalPtr; |
1718 |
for (i = 0; i < localCt; i++) { |
1719 |
if (!TclIsVarTemporary(localPtr)) { |
1720 |
char *localName = localPtr->name; |
1721 |
if ((nameBytes == localPtr->nameLength) |
1722 |
&& (strncmp(name, localName, (unsigned) nameBytes) == 0)) { |
1723 |
return i; |
1724 |
} |
1725 |
} |
1726 |
localPtr = localPtr->nextPtr; |
1727 |
} |
1728 |
} |
1729 |
|
1730 |
/* |
1731 |
* Create a new variable if appropriate. |
1732 |
*/ |
1733 |
|
1734 |
if (create || (name == NULL)) { |
1735 |
localVar = procPtr->numCompiledLocals; |
1736 |
localPtr = (CompiledLocal *) ckalloc((unsigned) |
1737 |
(sizeof(CompiledLocal) - sizeof(localPtr->name) |
1738 |
+ nameBytes+1)); |
1739 |
if (procPtr->firstLocalPtr == NULL) { |
1740 |
procPtr->firstLocalPtr = procPtr->lastLocalPtr = localPtr; |
1741 |
} else { |
1742 |
procPtr->lastLocalPtr->nextPtr = localPtr; |
1743 |
procPtr->lastLocalPtr = localPtr; |
1744 |
} |
1745 |
localPtr->nextPtr = NULL; |
1746 |
localPtr->nameLength = nameBytes; |
1747 |
localPtr->frameIndex = localVar; |
1748 |
localPtr->flags = flags; |
1749 |
if (name == NULL) { |
1750 |
localPtr->flags |= VAR_TEMPORARY; |
1751 |
} |
1752 |
localPtr->defValuePtr = NULL; |
1753 |
localPtr->resolveInfo = NULL; |
1754 |
|
1755 |
if (name != NULL) { |
1756 |
memcpy((VOID *) localPtr->name, (VOID *) name, |
1757 |
(size_t) nameBytes); |
1758 |
} |
1759 |
localPtr->name[nameBytes] = '\0'; |
1760 |
procPtr->numCompiledLocals++; |
1761 |
} |
1762 |
return localVar; |
1763 |
} |
1764 |
|
1765 |
/* |
1766 |
*---------------------------------------------------------------------- |
1767 |
* |
1768 |
* TclInitCompiledLocals -- |
1769 |
* |
1770 |
* This routine is invoked in order to initialize the compiled |
1771 |
* locals table for a new call frame. |
1772 |
* |
1773 |
* Results: |
1774 |
* None. |
1775 |
* |
1776 |
* Side effects: |
1777 |
* May invoke various name resolvers in order to determine which |
1778 |
* variables are being referenced at runtime. |
1779 |
* |
1780 |
*---------------------------------------------------------------------- |
1781 |
*/ |
1782 |
|
1783 |
void |
1784 |
TclInitCompiledLocals(interp, framePtr, nsPtr) |
1785 |
Tcl_Interp *interp; /* Current interpreter. */ |
1786 |
CallFrame *framePtr; /* Call frame to initialize. */ |
1787 |
Namespace *nsPtr; /* Pointer to current namespace. */ |
1788 |
{ |
1789 |
register CompiledLocal *localPtr; |
1790 |
Interp *iPtr = (Interp*) interp; |
1791 |
Tcl_ResolvedVarInfo *vinfo, *resVarInfo; |
1792 |
Var *varPtr = framePtr->compiledLocals; |
1793 |
Var *resolvedVarPtr; |
1794 |
ResolverScheme *resPtr; |
1795 |
int result; |
1796 |
|
1797 |
/* |
1798 |
* Initialize the array of local variables stored in the call frame. |
1799 |
* Some variables may have special resolution rules. In that case, |
1800 |
* we call their "resolver" procs to get our hands on the variable, |
1801 |
* and we make the compiled local a link to the real variable. |
1802 |
*/ |
1803 |
|
1804 |
for (localPtr = framePtr->procPtr->firstLocalPtr; |
1805 |
localPtr != NULL; |
1806 |
localPtr = localPtr->nextPtr) { |
1807 |
|
1808 |
/* |
1809 |
* Check to see if this local is affected by namespace or |
1810 |
* interp resolvers. The resolver to use is cached for the |
1811 |
* next invocation of the procedure. |
1812 |
*/ |
1813 |
|
1814 |
if (!(localPtr->flags & (VAR_ARGUMENT|VAR_TEMPORARY|VAR_RESOLVED)) |
1815 |
&& (nsPtr->compiledVarResProc || iPtr->resolverPtr)) { |
1816 |
resPtr = iPtr->resolverPtr; |
1817 |
|
1818 |
if (nsPtr->compiledVarResProc) { |
1819 |
result = (*nsPtr->compiledVarResProc)(nsPtr->interp, |
1820 |
localPtr->name, localPtr->nameLength, |
1821 |
(Tcl_Namespace *) nsPtr, &vinfo); |
1822 |
} else { |
1823 |
result = TCL_CONTINUE; |
1824 |
} |
1825 |
|
1826 |
while ((result == TCL_CONTINUE) && resPtr) { |
1827 |
if (resPtr->compiledVarResProc) { |
1828 |
result = (*resPtr->compiledVarResProc)(nsPtr->interp, |
1829 |
localPtr->name, localPtr->nameLength, |
1830 |
(Tcl_Namespace *) nsPtr, &vinfo); |
1831 |
} |
1832 |
resPtr = resPtr->nextPtr; |
1833 |
} |
1834 |
if (result == TCL_OK) { |
1835 |
localPtr->resolveInfo = vinfo; |
1836 |
localPtr->flags |= VAR_RESOLVED; |
1837 |
} |
1838 |
} |
1839 |
|
1840 |
/* |
1841 |
* Now invoke the resolvers to determine the exact variables that |
1842 |
* should be used. |
1843 |
*/ |
1844 |
|
1845 |
resVarInfo = localPtr->resolveInfo; |
1846 |
resolvedVarPtr = NULL; |
1847 |
|
1848 |
if (resVarInfo && resVarInfo->fetchProc) { |
1849 |
resolvedVarPtr = (Var*) (*resVarInfo->fetchProc)(interp, |
1850 |
resVarInfo); |
1851 |
} |
1852 |
|
1853 |
if (resolvedVarPtr) { |
1854 |
varPtr->name = localPtr->name; /* will be just '\0' if temp var */ |
1855 |
varPtr->nsPtr = NULL; |
1856 |
varPtr->hPtr = NULL; |
1857 |
varPtr->refCount = 0; |
1858 |
varPtr->tracePtr = NULL; |
1859 |
varPtr->searchPtr = NULL; |
1860 |
varPtr->flags = 0; |
1861 |
TclSetVarLink(varPtr); |
1862 |
varPtr->value.linkPtr = resolvedVarPtr; |
1863 |
resolvedVarPtr->refCount++; |
1864 |
} else { |
1865 |
varPtr->value.objPtr = NULL; |
1866 |
varPtr->name = localPtr->name; /* will be just '\0' if temp var */ |
1867 |
varPtr->nsPtr = NULL; |
1868 |
varPtr->hPtr = NULL; |
1869 |
varPtr->refCount = 0; |
1870 |
varPtr->tracePtr = NULL; |
1871 |
varPtr->searchPtr = NULL; |
1872 |
varPtr->flags = (localPtr->flags | VAR_UNDEFINED); |
1873 |
} |
1874 |
varPtr++; |
1875 |
} |
1876 |
} |
1877 |
|
1878 |
/* |
1879 |
*---------------------------------------------------------------------- |
1880 |
* |
1881 |
* TclExpandCodeArray -- |
1882 |
* |
1883 |
* Procedure that uses malloc to allocate more storage for a |
1884 |
* CompileEnv's code array. |
1885 |
* |
1886 |
* Results: |
1887 |
* None. |
1888 |
* |
1889 |
* Side effects: |
1890 |
* The byte code array in *envPtr is reallocated to a new array of |
1891 |
* double the size, and if envPtr->mallocedCodeArray is non-zero the |
1892 |
* old array is freed. Byte codes are copied from the old array to the |
1893 |
* new one. |
1894 |
* |
1895 |
*---------------------------------------------------------------------- |
1896 |
*/ |
1897 |
|
1898 |
void |
1899 |
TclExpandCodeArray(envPtr) |
1900 |
CompileEnv *envPtr; /* Points to the CompileEnv whose code array |
1901 |
* must be enlarged. */ |
1902 |
{ |
1903 |
/* |
1904 |
* envPtr->codeNext is equal to envPtr->codeEnd. The currently defined |
1905 |
* code bytes are stored between envPtr->codeStart and |
1906 |
* (envPtr->codeNext - 1) [inclusive]. |
1907 |
*/ |
1908 |
|
1909 |
size_t currBytes = (envPtr->codeNext - envPtr->codeStart); |
1910 |
size_t newBytes = 2*(envPtr->codeEnd - envPtr->codeStart); |
1911 |
unsigned char *newPtr = (unsigned char *) ckalloc((unsigned) newBytes); |
1912 |
|
1913 |
/* |
1914 |
* Copy from old code array to new, free old code array if needed, and |
1915 |
* mark new code array as malloced. |
1916 |
*/ |
1917 |
|
1918 |
memcpy((VOID *) newPtr, (VOID *) envPtr->codeStart, currBytes); |
1919 |
if (envPtr->mallocedCodeArray) { |
1920 |
ckfree((char *) envPtr->codeStart); |
1921 |
} |
1922 |
envPtr->codeStart = newPtr; |
1923 |
envPtr->codeNext = (newPtr + currBytes); |
1924 |
envPtr->codeEnd = (newPtr + newBytes); |
1925 |
envPtr->mallocedCodeArray = 1; |
1926 |
} |
1927 |
|
1928 |
/* |
1929 |
*---------------------------------------------------------------------- |
1930 |
* |
1931 |
* EnterCmdStartData -- |
1932 |
* |
1933 |
* Registers the starting source and bytecode location of a |
1934 |
* command. This information is used at runtime to map between |
1935 |
* instruction pc and source locations. |
1936 |
* |
1937 |
* Results: |
1938 |
* None. |
1939 |
* |
1940 |
* Side effects: |
1941 |
* Inserts source and code location information into the compilation |
1942 |
* environment envPtr for the command at index cmdIndex. The |
1943 |
* compilation environment's CmdLocation array is grown if necessary. |
1944 |
* |
1945 |
*---------------------------------------------------------------------- |
1946 |
*/ |
1947 |
|
1948 |
static void |
1949 |
EnterCmdStartData(envPtr, cmdIndex, srcOffset, codeOffset) |
1950 |
CompileEnv *envPtr; /* Points to the compilation environment |
1951 |
* structure in which to enter command |
1952 |
* location information. */ |
1953 |
int cmdIndex; /* Index of the command whose start data |
1954 |
* is being set. */ |
1955 |
int srcOffset; /* Offset of first char of the command. */ |
1956 |
int codeOffset; /* Offset of first byte of command code. */ |
1957 |
{ |
1958 |
CmdLocation *cmdLocPtr; |
1959 |
|
1960 |
if ((cmdIndex < 0) || (cmdIndex >= envPtr->numCommands)) { |
1961 |
panic("EnterCmdStartData: bad command index %d\n", cmdIndex); |
1962 |
} |
1963 |
|
1964 |
if (cmdIndex >= envPtr->cmdMapEnd) { |
1965 |
/* |
1966 |
* Expand the command location array by allocating more storage from |
1967 |
* the heap. The currently allocated CmdLocation entries are stored |
1968 |
* from cmdMapPtr[0] up to cmdMapPtr[envPtr->cmdMapEnd] (inclusive). |
1969 |
*/ |
1970 |
|
1971 |
size_t currElems = envPtr->cmdMapEnd; |
1972 |
size_t newElems = 2*currElems; |
1973 |
size_t currBytes = currElems * sizeof(CmdLocation); |
1974 |
size_t newBytes = newElems * sizeof(CmdLocation); |
1975 |
CmdLocation *newPtr = (CmdLocation *) ckalloc((unsigned) newBytes); |
1976 |
|
1977 |
/* |
1978 |
* Copy from old command location array to new, free old command |
1979 |
* location array if needed, and mark new array as malloced. |
1980 |
*/ |
1981 |
|
1982 |
memcpy((VOID *) newPtr, (VOID *) envPtr->cmdMapPtr, currBytes); |
1983 |
if (envPtr->mallocedCmdMap) { |
1984 |
ckfree((char *) envPtr->cmdMapPtr); |
1985 |
} |
1986 |
envPtr->cmdMapPtr = (CmdLocation *) newPtr; |
1987 |
envPtr->cmdMapEnd = newElems; |
1988 |
envPtr->mallocedCmdMap = 1; |
1989 |
} |
1990 |
|
1991 |
if (cmdIndex > 0) { |
1992 |
if (codeOffset < envPtr->cmdMapPtr[cmdIndex-1].codeOffset) { |
1993 |
panic("EnterCmdStartData: cmd map not sorted by code offset"); |
1994 |
} |
1995 |
} |
1996 |
|
1997 |
cmdLocPtr = &(envPtr->cmdMapPtr[cmdIndex]); |
1998 |
cmdLocPtr->codeOffset = codeOffset; |
1999 |
cmdLocPtr->srcOffset = srcOffset; |
2000 |
cmdLocPtr->numSrcBytes = -1; |
2001 |
cmdLocPtr->numCodeBytes = -1; |
2002 |
} |
2003 |
|
2004 |
/* |
2005 |
*---------------------------------------------------------------------- |
2006 |
* |
2007 |
* EnterCmdExtentData -- |
2008 |
* |
2009 |
* Registers the source and bytecode length for a command. This |
2010 |
* information is used at runtime to map between instruction pc and |
2011 |
* source locations. |
2012 |
* |
2013 |
* Results: |
2014 |
* None. |
2015 |
* |
2016 |
* Side effects: |
2017 |
* Inserts source and code length information into the compilation |
2018 |
* environment envPtr for the command at index cmdIndex. Starting |
2019 |
* source and bytecode information for the command must already |
2020 |
* have been registered. |
2021 |
* |
2022 |
*---------------------------------------------------------------------- |
2023 |
*/ |
2024 |
|
2025 |
static void |
2026 |
EnterCmdExtentData(envPtr, cmdIndex, numSrcBytes, numCodeBytes) |
2027 |
CompileEnv *envPtr; /* Points to the compilation environment |
2028 |
* structure in which to enter command |
2029 |
* location information. */ |
2030 |
int cmdIndex; /* Index of the command whose source and |
2031 |
* code length data is being set. */ |
2032 |
int numSrcBytes; /* Number of command source chars. */ |
2033 |
int numCodeBytes; /* Offset of last byte of command code. */ |
2034 |
{ |
2035 |
CmdLocation *cmdLocPtr; |
2036 |
|
2037 |
if ((cmdIndex < 0) || (cmdIndex >= envPtr->numCommands)) { |
2038 |
panic("EnterCmdExtentData: bad command index %d\n", cmdIndex); |
2039 |
} |
2040 |
|
2041 |
if (cmdIndex > envPtr->cmdMapEnd) { |
2042 |
panic("EnterCmdExtentData: missing start data for command %d\n", |
2043 |
cmdIndex); |
2044 |
} |
2045 |
|
2046 |
cmdLocPtr = &(envPtr->cmdMapPtr[cmdIndex]); |
2047 |
cmdLocPtr->numSrcBytes = numSrcBytes; |
2048 |
cmdLocPtr->numCodeBytes = numCodeBytes; |
2049 |
} |
2050 |
|
2051 |
/* |
2052 |
*---------------------------------------------------------------------- |
2053 |
* |
2054 |
* TclCreateExceptRange -- |
2055 |
* |
2056 |
* Procedure that allocates and initializes a new ExceptionRange |
2057 |
* structure of the specified kind in a CompileEnv. |
2058 |
* |
2059 |
* Results: |
2060 |
* Returns the index for the newly created ExceptionRange. |
2061 |
* |
2062 |
* Side effects: |
2063 |
* If there is not enough room in the CompileEnv's ExceptionRange |
2064 |
* array, the array in expanded: a new array of double the size is |
2065 |
* allocated, if envPtr->mallocedExceptArray is non-zero the old |
2066 |
* array is freed, and ExceptionRange entries are copied from the old |
2067 |
* array to the new one. |
2068 |
* |
2069 |
*---------------------------------------------------------------------- |
2070 |
*/ |
2071 |
|
2072 |
int |
2073 |
TclCreateExceptRange(type, envPtr) |
2074 |
ExceptionRangeType type; /* The kind of ExceptionRange desired. */ |
2075 |
register CompileEnv *envPtr;/* Points to CompileEnv for which to |
2076 |
* create a new ExceptionRange structure. */ |
2077 |
{ |
2078 |
register ExceptionRange *rangePtr; |
2079 |
int index = envPtr->exceptArrayNext; |
2080 |
|
2081 |
if (index >= envPtr->exceptArrayEnd) { |
2082 |
/* |
2083 |
* Expand the ExceptionRange array. The currently allocated entries |
2084 |
* are stored between elements 0 and (envPtr->exceptArrayNext - 1) |
2085 |
* [inclusive]. |
2086 |
*/ |
2087 |
|
2088 |
size_t currBytes = |
2089 |
envPtr->exceptArrayNext * sizeof(ExceptionRange); |
2090 |
int newElems = 2*envPtr->exceptArrayEnd; |
2091 |
size_t newBytes = newElems * sizeof(ExceptionRange); |
2092 |
ExceptionRange *newPtr = (ExceptionRange *) |
2093 |
ckalloc((unsigned) newBytes); |
2094 |
|
2095 |
/* |
2096 |
* Copy from old ExceptionRange array to new, free old |
2097 |
* ExceptionRange array if needed, and mark the new ExceptionRange |
2098 |
* array as malloced. |
2099 |
*/ |
2100 |
|
2101 |
memcpy((VOID *) newPtr, (VOID *) envPtr->exceptArrayPtr, |
2102 |
currBytes); |
2103 |
if (envPtr->mallocedExceptArray) { |
2104 |
ckfree((char *) envPtr->exceptArrayPtr); |
2105 |
} |
2106 |
envPtr->exceptArrayPtr = (ExceptionRange *) newPtr; |
2107 |
envPtr->exceptArrayEnd = newElems; |
2108 |
envPtr->mallocedExceptArray = 1; |
2109 |
} |
2110 |
envPtr->exceptArrayNext++; |
2111 |
|
2112 |
rangePtr = &(envPtr->exceptArrayPtr[index]); |
2113 |
rangePtr->type = type; |
2114 |
rangePtr->nestingLevel = envPtr->exceptDepth; |
2115 |
rangePtr->codeOffset = -1; |
2116 |
rangePtr->numCodeBytes = -1; |
2117 |
rangePtr->breakOffset = -1; |
2118 |
rangePtr->continueOffset = -1; |
2119 |
rangePtr->catchOffset = -1; |
2120 |
return index; |
2121 |
} |
2122 |
|
2123 |
/* |
2124 |
*---------------------------------------------------------------------- |
2125 |
* |
2126 |
* TclCreateAuxData -- |
2127 |
* |
2128 |
* Procedure that allocates and initializes a new AuxData structure in |
2129 |
* a CompileEnv's array of compilation auxiliary data records. These |
2130 |
* AuxData records hold information created during compilation by |
2131 |
* CompileProcs and used by instructions during execution. |
2132 |
* |
2133 |
* Results: |
2134 |
* Returns the index for the newly created AuxData structure. |
2135 |
* |
2136 |
* Side effects: |
2137 |
* If there is not enough room in the CompileEnv's AuxData array, |
2138 |
* the AuxData array in expanded: a new array of double the size |
2139 |
* is allocated, if envPtr->mallocedAuxDataArray is non-zero |
2140 |
* the old array is freed, and AuxData entries are copied from |
2141 |
* the old array to the new one. |
2142 |
* |
2143 |
*---------------------------------------------------------------------- |
2144 |
*/ |
2145 |
|
2146 |
int |
2147 |
TclCreateAuxData(clientData, typePtr, envPtr) |
2148 |
ClientData clientData; /* The compilation auxiliary data to store |
2149 |
* in the new aux data record. */ |
2150 |
AuxDataType *typePtr; /* Pointer to the type to attach to this AuxData */ |
2151 |
register CompileEnv *envPtr;/* Points to the CompileEnv for which a new |
2152 |
* aux data structure is to be allocated. */ |
2153 |
{ |
2154 |
int index; /* Index for the new AuxData structure. */ |
2155 |
register AuxData *auxDataPtr; |
2156 |
/* Points to the new AuxData structure */ |
2157 |
|
2158 |
index = envPtr->auxDataArrayNext; |
2159 |
if (index >= envPtr->auxDataArrayEnd) { |
2160 |
/* |
2161 |
* Expand the AuxData array. The currently allocated entries are |
2162 |
* stored between elements 0 and (envPtr->auxDataArrayNext - 1) |
2163 |
* [inclusive]. |
2164 |
*/ |
2165 |
|
2166 |
size_t currBytes = envPtr->auxDataArrayNext * sizeof(AuxData); |
2167 |
int newElems = 2*envPtr->auxDataArrayEnd; |
2168 |
size_t newBytes = newElems * sizeof(AuxData); |
2169 |
AuxData *newPtr = (AuxData *) ckalloc((unsigned) newBytes); |
2170 |
|
2171 |
/* |
2172 |
* Copy from old AuxData array to new, free old AuxData array if |
2173 |
* needed, and mark the new AuxData array as malloced. |
2174 |
*/ |
2175 |
|
2176 |
memcpy((VOID *) newPtr, (VOID *) envPtr->auxDataArrayPtr, |
2177 |
currBytes); |
2178 |
if (envPtr->mallocedAuxDataArray) { |
2179 |
ckfree((char *) envPtr->auxDataArrayPtr); |
2180 |
} |
2181 |
envPtr->auxDataArrayPtr = newPtr; |
2182 |
envPtr->auxDataArrayEnd = newElems; |
2183 |
envPtr->mallocedAuxDataArray = 1; |
2184 |
} |
2185 |
envPtr->auxDataArrayNext++; |
2186 |
|
2187 |
auxDataPtr = &(envPtr->auxDataArrayPtr[index]); |
2188 |
auxDataPtr->clientData = clientData; |
2189 |
auxDataPtr->type = typePtr; |
2190 |
return index; |
2191 |
} |
2192 |
|
2193 |
/* |
2194 |
*---------------------------------------------------------------------- |
2195 |
* |
2196 |
* TclInitJumpFixupArray -- |
2197 |
* |
2198 |
* Initializes a JumpFixupArray structure to hold some number of |
2199 |
* jump fixup entries. |
2200 |
* |
2201 |
* Results: |
2202 |
* None. |
2203 |
* |
2204 |
* Side effects: |
2205 |
* The JumpFixupArray structure is initialized. |
2206 |
* |
2207 |
*---------------------------------------------------------------------- |
2208 |
*/ |
2209 |
|
2210 |
void |
2211 |
TclInitJumpFixupArray(fixupArrayPtr) |
2212 |
register JumpFixupArray *fixupArrayPtr; |
2213 |
/* Points to the JumpFixupArray structure |
2214 |
* to initialize. */ |
2215 |
{ |
2216 |
fixupArrayPtr->fixup = fixupArrayPtr->staticFixupSpace; |
2217 |
fixupArrayPtr->next = 0; |
2218 |
fixupArrayPtr->end = (JUMPFIXUP_INIT_ENTRIES - 1); |
2219 |
fixupArrayPtr->mallocedArray = 0; |
2220 |
} |
2221 |
|
2222 |
/* |
2223 |
*---------------------------------------------------------------------- |
2224 |
* |
2225 |
* TclExpandJumpFixupArray -- |
2226 |
* |
2227 |
* Procedure that uses malloc to allocate more storage for a |
2228 |
* jump fixup array. |
2229 |
* |
2230 |
* Results: |
2231 |
* None. |
2232 |
* |
2233 |
* Side effects: |
2234 |
* The jump fixup array in *fixupArrayPtr is reallocated to a new array |
2235 |
* of double the size, and if fixupArrayPtr->mallocedArray is non-zero |
2236 |
* the old array is freed. Jump fixup structures are copied from the |
2237 |
* old array to the new one. |
2238 |
* |
2239 |
*---------------------------------------------------------------------- |
2240 |
*/ |
2241 |
|
2242 |
void |
2243 |
TclExpandJumpFixupArray(fixupArrayPtr) |
2244 |
register JumpFixupArray *fixupArrayPtr; |
2245 |
/* Points to the JumpFixupArray structure |
2246 |
* to enlarge. */ |
2247 |
{ |
2248 |
/* |
2249 |
* The currently allocated jump fixup entries are stored from fixup[0] |
2250 |
* up to fixup[fixupArrayPtr->fixupNext] (*not* inclusive). We assume |
2251 |
* fixupArrayPtr->fixupNext is equal to fixupArrayPtr->fixupEnd. |
2252 |
*/ |
2253 |
|
2254 |
size_t currBytes = fixupArrayPtr->next * sizeof(JumpFixup); |
2255 |
int newElems = 2*(fixupArrayPtr->end + 1); |
2256 |
size_t newBytes = newElems * sizeof(JumpFixup); |
2257 |
JumpFixup *newPtr = (JumpFixup *) ckalloc((unsigned) newBytes); |
2258 |
|
2259 |
/* |
2260 |
* Copy from the old array to new, free the old array if needed, |
2261 |
* and mark the new array as malloced. |
2262 |
*/ |
2263 |
|
2264 |
memcpy((VOID *) newPtr, (VOID *) fixupArrayPtr->fixup, currBytes); |
2265 |
if (fixupArrayPtr->mallocedArray) { |
2266 |
ckfree((char *) fixupArrayPtr->fixup); |
2267 |
} |
2268 |
fixupArrayPtr->fixup = (JumpFixup *) newPtr; |
2269 |
fixupArrayPtr->end = newElems; |
2270 |
fixupArrayPtr->mallocedArray = 1; |
2271 |
} |
2272 |
|
2273 |
/* |
2274 |
*---------------------------------------------------------------------- |
2275 |
* |
2276 |
* TclFreeJumpFixupArray -- |
2277 |
* |
2278 |
* Free any storage allocated in a jump fixup array structure. |
2279 |
* |
2280 |
* Results: |
2281 |
* None. |
2282 |
* |
2283 |
* Side effects: |
2284 |
* Allocated storage in the JumpFixupArray structure is freed. |
2285 |
* |
2286 |
*---------------------------------------------------------------------- |
2287 |
*/ |
2288 |
|
2289 |
void |
2290 |
TclFreeJumpFixupArray(fixupArrayPtr) |
2291 |
register JumpFixupArray *fixupArrayPtr; |
2292 |
/* Points to the JumpFixupArray structure |
2293 |
* to free. */ |
2294 |
{ |
2295 |
if (fixupArrayPtr->mallocedArray) { |
2296 |
ckfree((char *) fixupArrayPtr->fixup); |
2297 |
} |
2298 |
} |
2299 |
|
2300 |
/* |
2301 |
*---------------------------------------------------------------------- |
2302 |
* |
2303 |
* TclEmitForwardJump -- |
2304 |
* |
2305 |
* Procedure to emit a two-byte forward jump of kind "jumpType". Since |
2306 |
* the jump may later have to be grown to five bytes if the jump target |
2307 |
* is more than, say, 127 bytes away, this procedure also initializes a |
2308 |
* JumpFixup record with information about the jump. |
2309 |
* |
2310 |
* Results: |
2311 |
* None. |
2312 |
* |
2313 |
* Side effects: |
2314 |
* The JumpFixup record pointed to by "jumpFixupPtr" is initialized |
2315 |
* with information needed later if the jump is to be grown. Also, |
2316 |
* a two byte jump of the designated type is emitted at the current |
2317 |
* point in the bytecode stream. |
2318 |
* |
2319 |
*---------------------------------------------------------------------- |
2320 |
*/ |
2321 |
|
2322 |
void |
2323 |
TclEmitForwardJump(envPtr, jumpType, jumpFixupPtr) |
2324 |
CompileEnv *envPtr; /* Points to the CompileEnv structure that |
2325 |
* holds the resulting instruction. */ |
2326 |
TclJumpType jumpType; /* Indicates the kind of jump: if true or |
2327 |
* false or unconditional. */ |
2328 |
JumpFixup *jumpFixupPtr; /* Points to the JumpFixup structure to |
2329 |
* initialize with information about this |
2330 |
* forward jump. */ |
2331 |
{ |
2332 |
/* |
2333 |
* Initialize the JumpFixup structure: |
2334 |
* - codeOffset is offset of first byte of jump below |
2335 |
* - cmdIndex is index of the command after the current one |
2336 |
* - exceptIndex is the index of the first ExceptionRange after |
2337 |
* the current one. |
2338 |
*/ |
2339 |
|
2340 |
jumpFixupPtr->jumpType = jumpType; |
2341 |
jumpFixupPtr->codeOffset = (envPtr->codeNext - envPtr->codeStart); |
2342 |
jumpFixupPtr->cmdIndex = envPtr->numCommands; |
2343 |
jumpFixupPtr->exceptIndex = envPtr->exceptArrayNext; |
2344 |
|
2345 |
switch (jumpType) { |
2346 |
case TCL_UNCONDITIONAL_JUMP: |
2347 |
TclEmitInstInt1(INST_JUMP1, 0, envPtr); |
2348 |
break; |
2349 |
case TCL_TRUE_JUMP: |
2350 |
TclEmitInstInt1(INST_JUMP_TRUE1, 0, envPtr); |
2351 |
break; |
2352 |
default: |
2353 |
TclEmitInstInt1(INST_JUMP_FALSE1, 0, envPtr); |
2354 |
break; |
2355 |
} |
2356 |
} |
2357 |
|
2358 |
/* |
2359 |
*---------------------------------------------------------------------- |
2360 |
* |
2361 |
* TclFixupForwardJump -- |
2362 |
* |
2363 |
* Procedure that updates a previously-emitted forward jump to jump |
2364 |
* a specified number of bytes, "jumpDist". If necessary, the jump is |
2365 |
* grown from two to five bytes; this is done if the jump distance is |
2366 |
* greater than "distThreshold" (normally 127 bytes). The jump is |
2367 |
* described by a JumpFixup record previously initialized by |
2368 |
* TclEmitForwardJump. |
2369 |
* |
2370 |
* Results: |
2371 |
* 1 if the jump was grown and subsequent instructions had to be moved; |
2372 |
* otherwise 0. This result is returned to allow callers to update |
2373 |
* any additional code offsets they may hold. |
2374 |
* |
2375 |
* Side effects: |
2376 |
* The jump may be grown and subsequent instructions moved. If this |
2377 |
* happens, the code offsets for any commands and any ExceptionRange |
2378 |
* records between the jump and the current code address will be |
2379 |
* updated to reflect the moved code. Also, the bytecode instruction |
2380 |
* array in the CompileEnv structure may be grown and reallocated. |
2381 |
* |
2382 |
*---------------------------------------------------------------------- |
2383 |
*/ |
2384 |
|
2385 |
int |
2386 |
TclFixupForwardJump(envPtr, jumpFixupPtr, jumpDist, distThreshold) |
2387 |
CompileEnv *envPtr; /* Points to the CompileEnv structure that |
2388 |
* holds the resulting instruction. */ |
2389 |
JumpFixup *jumpFixupPtr; /* Points to the JumpFixup structure that |
2390 |
* describes the forward jump. */ |
2391 |
int jumpDist; /* Jump distance to set in jump |
2392 |
* instruction. */ |
2393 |
int distThreshold; /* Maximum distance before the two byte |
2394 |
* jump is grown to five bytes. */ |
2395 |
{ |
2396 |
unsigned char *jumpPc, *p; |
2397 |
int firstCmd, lastCmd, firstRange, lastRange, k; |
2398 |
unsigned int numBytes; |
2399 |
|
2400 |
if (jumpDist <= distThreshold) { |
2401 |
jumpPc = (envPtr->codeStart + jumpFixupPtr->codeOffset); |
2402 |
switch (jumpFixupPtr->jumpType) { |
2403 |
case TCL_UNCONDITIONAL_JUMP: |
2404 |
TclUpdateInstInt1AtPc(INST_JUMP1, jumpDist, jumpPc); |
2405 |
break; |
2406 |
case TCL_TRUE_JUMP: |
2407 |
TclUpdateInstInt1AtPc(INST_JUMP_TRUE1, jumpDist, jumpPc); |
2408 |
break; |
2409 |
default: |
2410 |
TclUpdateInstInt1AtPc(INST_JUMP_FALSE1, jumpDist, jumpPc); |
2411 |
break; |
2412 |
} |
2413 |
return 0; |
2414 |
} |
2415 |
|
2416 |
/* |
2417 |
* We must grow the jump then move subsequent instructions down. |
2418 |
* Note that if we expand the space for generated instructions, |
2419 |
* code addresses might change; be careful about updating any of |
2420 |
* these addresses held in variables. |
2421 |
*/ |
2422 |
|
2423 |
if ((envPtr->codeNext + 3) > envPtr->codeEnd) { |
2424 |
TclExpandCodeArray(envPtr); |
2425 |
} |
2426 |
jumpPc = (envPtr->codeStart + jumpFixupPtr->codeOffset); |
2427 |
for (numBytes = envPtr->codeNext-jumpPc-2, p = jumpPc+2+numBytes-1; |
2428 |
numBytes > 0; numBytes--, p--) { |
2429 |
p[3] = p[0]; |
2430 |
} |
2431 |
envPtr->codeNext += 3; |
2432 |
jumpDist += 3; |
2433 |
switch (jumpFixupPtr->jumpType) { |
2434 |
case TCL_UNCONDITIONAL_JUMP: |
2435 |
TclUpdateInstInt4AtPc(INST_JUMP4, jumpDist, jumpPc); |
2436 |
break; |
2437 |
case TCL_TRUE_JUMP: |
2438 |
TclUpdateInstInt4AtPc(INST_JUMP_TRUE4, jumpDist, jumpPc); |
2439 |
break; |
2440 |
default: |
2441 |
TclUpdateInstInt4AtPc(INST_JUMP_FALSE4, jumpDist, jumpPc); |
2442 |
break; |
2443 |
} |
2444 |
|
2445 |
/* |
2446 |
* Adjust the code offsets for any commands and any ExceptionRange |
2447 |
* records between the jump and the current code address. |
2448 |
*/ |
2449 |
|
2450 |
firstCmd = jumpFixupPtr->cmdIndex; |
2451 |
lastCmd = (envPtr->numCommands - 1); |
2452 |
if (firstCmd < lastCmd) { |
2453 |
for (k = firstCmd; k <= lastCmd; k++) { |
2454 |
(envPtr->cmdMapPtr[k]).codeOffset += 3; |
2455 |
} |
2456 |
} |
2457 |
|
2458 |
firstRange = jumpFixupPtr->exceptIndex; |
2459 |
lastRange = (envPtr->exceptArrayNext - 1); |
2460 |
for (k = firstRange; k <= lastRange; k++) { |
2461 |
ExceptionRange *rangePtr = &(envPtr->exceptArrayPtr[k]); |
2462 |
rangePtr->codeOffset += 3; |
2463 |
|
2464 |
switch (rangePtr->type) { |
2465 |
case LOOP_EXCEPTION_RANGE: |
2466 |
rangePtr->breakOffset += 3; |
2467 |
if (rangePtr->continueOffset != -1) { |
2468 |
rangePtr->continueOffset += 3; |
2469 |
} |
2470 |
break; |
2471 |
case CATCH_EXCEPTION_RANGE: |
2472 |
rangePtr->catchOffset += 3; |
2473 |
break; |
2474 |
default: |
2475 |
panic("TclFixupForwardJump: bad ExceptionRange type %d\n", |
2476 |
rangePtr->type); |
2477 |
} |
2478 |
} |
2479 |
return 1; /* the jump was grown */ |
2480 |
} |
2481 |
|
2482 |
/* |
2483 |
*---------------------------------------------------------------------- |
2484 |
* |
2485 |
* TclGetInstructionTable -- |
2486 |
* |
2487 |
* Returns a pointer to the table describing Tcl bytecode instructions. |
2488 |
* This procedure is defined so that clients can access the pointer from |
2489 |
* outside the TCL DLLs. |
2490 |
* |
2491 |
* Results: |
2492 |
* Returns a pointer to the global instruction table, same as the |
2493 |
* expression (&instructionTable[0]). |
2494 |
* |
2495 |
* Side effects: |
2496 |
* None. |
2497 |
* |
2498 |
*---------------------------------------------------------------------- |
2499 |
*/ |
2500 |
|
2501 |
InstructionDesc * |
2502 |
TclGetInstructionTable() |
2503 |
{ |
2504 |
return &instructionTable[0]; |
2505 |
} |
2506 |
|
2507 |
/* |
2508 |
*-------------------------------------------------------------- |
2509 |
* |
2510 |
* TclRegisterAuxDataType -- |
2511 |
* |
2512 |
* This procedure is called to register a new AuxData type |
2513 |
* in the table of all AuxData types supported by Tcl. |
2514 |
* |
2515 |
* Results: |
2516 |
* None. |
2517 |
* |
2518 |
* Side effects: |
2519 |
* The type is registered in the AuxData type table. If there was already |
2520 |
* a type with the same name as in typePtr, it is replaced with the |
2521 |
* new type. |
2522 |
* |
2523 |
*-------------------------------------------------------------- |
2524 |
*/ |
2525 |
|
2526 |
void |
2527 |
TclRegisterAuxDataType(typePtr) |
2528 |
AuxDataType *typePtr; /* Information about object type; |
2529 |
* storage must be statically |
2530 |
* allocated (must live forever). */ |
2531 |
{ |
2532 |
register Tcl_HashEntry *hPtr; |
2533 |
int new; |
2534 |
|
2535 |
Tcl_MutexLock(&tableMutex); |
2536 |
if (!auxDataTypeTableInitialized) { |
2537 |
TclInitAuxDataTypeTable(); |
2538 |
} |
2539 |
|
2540 |
/* |
2541 |
* If there's already a type with the given name, remove it. |
2542 |
*/ |
2543 |
|
2544 |
hPtr = Tcl_FindHashEntry(&auxDataTypeTable, typePtr->name); |
2545 |
if (hPtr != (Tcl_HashEntry *) NULL) { |
2546 |
Tcl_DeleteHashEntry(hPtr); |
2547 |
} |
2548 |
|
2549 |
/* |
2550 |
* Now insert the new object type. |
2551 |
*/ |
2552 |
|
2553 |
hPtr = Tcl_CreateHashEntry(&auxDataTypeTable, typePtr->name, &new); |
2554 |
if (new) { |
2555 |
Tcl_SetHashValue(hPtr, typePtr); |
2556 |
} |
2557 |
Tcl_MutexUnlock(&tableMutex); |
2558 |
} |
2559 |
|
2560 |
/* |
2561 |
*---------------------------------------------------------------------- |
2562 |
* |
2563 |
* TclGetAuxDataType -- |
2564 |
* |
2565 |
* This procedure looks up an Auxdata type by name. |
2566 |
* |
2567 |
* Results: |
2568 |
* If an AuxData type with name matching "typeName" is found, a pointer |
2569 |
* to its AuxDataType structure is returned; otherwise, NULL is returned. |
2570 |
* |
2571 |
* Side effects: |
2572 |
* None. |
2573 |
* |
2574 |
*---------------------------------------------------------------------- |
2575 |
*/ |
2576 |
|
2577 |
AuxDataType * |
2578 |
TclGetAuxDataType(typeName) |
2579 |
char *typeName; /* Name of AuxData type to look up. */ |
2580 |
{ |
2581 |
register Tcl_HashEntry *hPtr; |
2582 |
AuxDataType *typePtr = NULL; |
2583 |
|
2584 |
Tcl_MutexLock(&tableMutex); |
2585 |
if (!auxDataTypeTableInitialized) { |
2586 |
TclInitAuxDataTypeTable(); |
2587 |
} |
2588 |
|
2589 |
hPtr = Tcl_FindHashEntry(&auxDataTypeTable, typeName); |
2590 |
if (hPtr != (Tcl_HashEntry *) NULL) { |
2591 |
typePtr = (AuxDataType *) Tcl_GetHashValue(hPtr); |
2592 |
} |
2593 |
Tcl_MutexUnlock(&tableMutex); |
2594 |
|
2595 |
return typePtr; |
2596 |
} |
2597 |
|
2598 |
/* |
2599 |
*-------------------------------------------------------------- |
2600 |
* |
2601 |
* TclInitAuxDataTypeTable -- |
2602 |
* |
2603 |
* This procedure is invoked to perform once-only initialization of |
2604 |
* the AuxData type table. It also registers the AuxData types defined in |
2605 |
* this file. |
2606 |
* |
2607 |
* Results: |
2608 |
* None. |
2609 |
* |
2610 |
* Side effects: |
2611 |
* Initializes the table of defined AuxData types "auxDataTypeTable" with |
2612 |
* builtin AuxData types defined in this file. |
2613 |
* |
2614 |
*-------------------------------------------------------------- |
2615 |
*/ |
2616 |
|
2617 |
void |
2618 |
TclInitAuxDataTypeTable() |
2619 |
{ |
2620 |
/* |
2621 |
* The table mutex must already be held before this routine is invoked. |
2622 |
*/ |
2623 |
|
2624 |
auxDataTypeTableInitialized = 1; |
2625 |
Tcl_InitHashTable(&auxDataTypeTable, TCL_STRING_KEYS); |
2626 |
|
2627 |
/* |
2628 |
* There is only one AuxData type at this time, so register it here. |
2629 |
*/ |
2630 |
|
2631 |
TclRegisterAuxDataType(&tclForeachInfoType); |
2632 |
} |
2633 |
|
2634 |
/* |
2635 |
*---------------------------------------------------------------------- |
2636 |
* |
2637 |
* TclFinalizeAuxDataTypeTable -- |
2638 |
* |
2639 |
* This procedure is called by Tcl_Finalize after all exit handlers |
2640 |
* have been run to free up storage associated with the table of AuxData |
2641 |
* types. This procedure is called by TclFinalizeExecution() which |
2642 |
* is called by Tcl_Finalize(). |
2643 |
* |
2644 |
* Results: |
2645 |
* None. |
2646 |
* |
2647 |
* Side effects: |
2648 |
* Deletes all entries in the hash table of AuxData types. |
2649 |
* |
2650 |
*---------------------------------------------------------------------- |
2651 |
*/ |
2652 |
|
2653 |
void |
2654 |
TclFinalizeAuxDataTypeTable() |
2655 |
{ |
2656 |
Tcl_MutexLock(&tableMutex); |
2657 |
if (auxDataTypeTableInitialized) { |
2658 |
Tcl_DeleteHashTable(&auxDataTypeTable); |
2659 |
auxDataTypeTableInitialized = 0; |
2660 |
} |
2661 |
Tcl_MutexUnlock(&tableMutex); |
2662 |
} |
2663 |
|
2664 |
/* |
2665 |
*---------------------------------------------------------------------- |
2666 |
* |
2667 |
* GetCmdLocEncodingSize -- |
2668 |
* |
2669 |
* Computes the total number of bytes needed to encode the command |
2670 |
* location information for some compiled code. |
2671 |
* |
2672 |
* Results: |
2673 |
* The byte count needed to encode the compiled location information. |
2674 |
* |
2675 |
* Side effects: |
2676 |
* None. |
2677 |
* |
2678 |
*---------------------------------------------------------------------- |
2679 |
*/ |
2680 |
|
2681 |
static int |
2682 |
GetCmdLocEncodingSize(envPtr) |
2683 |
CompileEnv *envPtr; /* Points to compilation environment |
2684 |
* structure containing the CmdLocation |
2685 |
* structure to encode. */ |
2686 |
{ |
2687 |
register CmdLocation *mapPtr = envPtr->cmdMapPtr; |
2688 |
int numCmds = envPtr->numCommands; |
2689 |
int codeDelta, codeLen, srcDelta, srcLen; |
2690 |
int codeDeltaNext, codeLengthNext, srcDeltaNext, srcLengthNext; |
2691 |
/* The offsets in their respective byte |
2692 |
* sequences where the next encoded offset |
2693 |
* or length should go. */ |
2694 |
int prevCodeOffset, prevSrcOffset, i; |
2695 |
|
2696 |
codeDeltaNext = codeLengthNext = srcDeltaNext = srcLengthNext = 0; |
2697 |
prevCodeOffset = prevSrcOffset = 0; |
2698 |
for (i = 0; i < numCmds; i++) { |
2699 |
codeDelta = (mapPtr[i].codeOffset - prevCodeOffset); |
2700 |
if (codeDelta < 0) { |
2701 |
panic("GetCmdLocEncodingSize: bad code offset"); |
2702 |
} else if (codeDelta <= 127) { |
2703 |
codeDeltaNext++; |
2704 |
} else { |
2705 |
codeDeltaNext += 5; /* 1 byte for 0xFF, 4 for positive delta */ |
2706 |
} |
2707 |
prevCodeOffset = mapPtr[i].codeOffset; |
2708 |
|
2709 |
codeLen = mapPtr[i].numCodeBytes; |
2710 |
if (codeLen < 0) { |
2711 |
panic("GetCmdLocEncodingSize: bad code length"); |
2712 |
} else if (codeLen <= 127) { |
2713 |
codeLengthNext++; |
2714 |
} else { |
2715 |
codeLengthNext += 5; /* 1 byte for 0xFF, 4 for length */ |
2716 |
} |
2717 |
|
2718 |
srcDelta = (mapPtr[i].srcOffset - prevSrcOffset); |
2719 |
if ((-127 <= srcDelta) && (srcDelta <= 127)) { |
2720 |
srcDeltaNext++; |
2721 |
} else { |
2722 |
srcDeltaNext += 5; /* 1 byte for 0xFF, 4 for delta */ |
2723 |
} |
2724 |
prevSrcOffset = mapPtr[i].srcOffset; |
2725 |
|
2726 |
srcLen = mapPtr[i].numSrcBytes; |
2727 |
if (srcLen < 0) { |
2728 |
panic("GetCmdLocEncodingSize: bad source length"); |
2729 |
} else if (srcLen <= 127) { |
2730 |
srcLengthNext++; |
2731 |
} else { |
2732 |
srcLengthNext += 5; /* 1 byte for 0xFF, 4 for length */ |
2733 |
} |
2734 |
} |
2735 |
|
2736 |
return (codeDeltaNext + codeLengthNext + srcDeltaNext + srcLengthNext); |
2737 |
} |
2738 |
|
2739 |
/* |
2740 |
*---------------------------------------------------------------------- |
2741 |
* |
2742 |
* EncodeCmdLocMap -- |
2743 |
* |
2744 |
* Encode the command location information for some compiled code into |
2745 |
* a ByteCode structure. The encoded command location map is stored as |
2746 |
* three adjacent byte sequences. |
2747 |
* |
2748 |
* Results: |
2749 |
* Pointer to the first byte after the encoded command location |
2750 |
* information. |
2751 |
* |
2752 |
* Side effects: |
2753 |
* The encoded information is stored into the block of memory headed |
2754 |
* by codePtr. Also records pointers to the start of the four byte |
2755 |
* sequences in fields in codePtr's ByteCode header structure. |
2756 |
* |
2757 |
*---------------------------------------------------------------------- |
2758 |
*/ |
2759 |
|
2760 |
static unsigned char * |
2761 |
EncodeCmdLocMap(envPtr, codePtr, startPtr) |
2762 |
CompileEnv *envPtr; /* Points to compilation environment |
2763 |
* structure containing the CmdLocation |
2764 |
* structure to encode. */ |
2765 |
ByteCode *codePtr; /* ByteCode in which to encode envPtr's |
2766 |
* command location information. */ |
2767 |
unsigned char *startPtr; /* Points to the first byte in codePtr's |
2768 |
* memory block where the location |
2769 |
* information is to be stored. */ |
2770 |
{ |
2771 |
register CmdLocation *mapPtr = envPtr->cmdMapPtr; |
2772 |
int numCmds = envPtr->numCommands; |
2773 |
register unsigned char *p = startPtr; |
2774 |
int codeDelta, codeLen, srcDelta, srcLen, prevOffset; |
2775 |
register int i; |
2776 |
|
2777 |
/* |
2778 |
* Encode the code offset for each command as a sequence of deltas. |
2779 |
*/ |
2780 |
|
2781 |
codePtr->codeDeltaStart = p; |
2782 |
prevOffset = 0; |
2783 |
for (i = 0; i < numCmds; i++) { |
2784 |
codeDelta = (mapPtr[i].codeOffset - prevOffset); |
2785 |
if (codeDelta < 0) { |
2786 |
panic("EncodeCmdLocMap: bad code offset"); |
2787 |
} else if (codeDelta <= 127) { |
2788 |
TclStoreInt1AtPtr(codeDelta, p); |
2789 |
p++; |
2790 |
} else { |
2791 |
TclStoreInt1AtPtr(0xFF, p); |
2792 |
p++; |
2793 |
TclStoreInt4AtPtr(codeDelta, p); |
2794 |
p += 4; |
2795 |
} |
2796 |
prevOffset = mapPtr[i].codeOffset; |
2797 |
} |
2798 |
|
2799 |
/* |
2800 |
* Encode the code length for each command. |
2801 |
*/ |
2802 |
|
2803 |
codePtr->codeLengthStart = p; |
2804 |
for (i = 0; i < numCmds; i++) { |
2805 |
codeLen = mapPtr[i].numCodeBytes; |
2806 |
if (codeLen < 0) { |
2807 |
panic("EncodeCmdLocMap: bad code length"); |
2808 |
} else if (codeLen <= 127) { |
2809 |
TclStoreInt1AtPtr(codeLen, p); |
2810 |
p++; |
2811 |
} else { |
2812 |
TclStoreInt1AtPtr(0xFF, p); |
2813 |
p++; |
2814 |
TclStoreInt4AtPtr(codeLen, p); |
2815 |
p += 4; |
2816 |
} |
2817 |
} |
2818 |
|
2819 |
/* |
2820 |
* Encode the source offset for each command as a sequence of deltas. |
2821 |
*/ |
2822 |
|
2823 |
codePtr->srcDeltaStart = p; |
2824 |
prevOffset = 0; |
2825 |
for (i = 0; i < numCmds; i++) { |
2826 |
srcDelta = (mapPtr[i].srcOffset - prevOffset); |
2827 |
if ((-127 <= srcDelta) && (srcDelta <= 127)) { |
2828 |
TclStoreInt1AtPtr(srcDelta, p); |
2829 |
p++; |
2830 |
} else { |
2831 |
TclStoreInt1AtPtr(0xFF, p); |
2832 |
p++; |
2833 |
TclStoreInt4AtPtr(srcDelta, p); |
2834 |
p += 4; |
2835 |
} |
2836 |
prevOffset = mapPtr[i].srcOffset; |
2837 |
} |
2838 |
|
2839 |
/* |
2840 |
* Encode the source length for each command. |
2841 |
*/ |
2842 |
|
2843 |
codePtr->srcLengthStart = p; |
2844 |
for (i = 0; i < numCmds; i++) { |
2845 |
srcLen = mapPtr[i].numSrcBytes; |
2846 |
if (srcLen < 0) { |
2847 |
panic("EncodeCmdLocMap: bad source length"); |
2848 |
} else if (srcLen <= 127) { |
2849 |
TclStoreInt1AtPtr(srcLen, p); |
2850 |
p++; |
2851 |
} else { |
2852 |
TclStoreInt1AtPtr(0xFF, p); |
2853 |
p++; |
2854 |
TclStoreInt4AtPtr(srcLen, p); |
2855 |
p += 4; |
2856 |
} |
2857 |
} |
2858 |
|
2859 |
return p; |
2860 |
} |
2861 |
|
2862 |
#ifdef TCL_COMPILE_DEBUG |
2863 |
/* |
2864 |
*---------------------------------------------------------------------- |
2865 |
* |
2866 |
* TclPrintByteCodeObj -- |
2867 |
* |
2868 |
* This procedure prints ("disassembles") the instructions of a |
2869 |
* bytecode object to stdout. |
2870 |
* |
2871 |
* Results: |
2872 |
* None. |
2873 |
* |
2874 |
* Side effects: |
2875 |
* None. |
2876 |
* |
2877 |
*---------------------------------------------------------------------- |
2878 |
*/ |
2879 |
|
2880 |
void |
2881 |
TclPrintByteCodeObj(interp, objPtr) |
2882 |
Tcl_Interp *interp; /* Used only for Tcl_GetStringFromObj. */ |
2883 |
Tcl_Obj *objPtr; /* The bytecode object to disassemble. */ |
2884 |
{ |
2885 |
ByteCode* codePtr = (ByteCode *) objPtr->internalRep.otherValuePtr; |
2886 |
unsigned char *codeStart, *codeLimit, *pc; |
2887 |
unsigned char *codeDeltaNext, *codeLengthNext; |
2888 |
unsigned char *srcDeltaNext, *srcLengthNext; |
2889 |
int codeOffset, codeLen, srcOffset, srcLen, numCmds, delta, i; |
2890 |
Interp *iPtr = (Interp *) *codePtr->interpHandle; |
2891 |
|
2892 |
if (codePtr->refCount <= 0) { |
2893 |
return; /* already freed */ |
2894 |
} |
2895 |
|
2896 |
codeStart = codePtr->codeStart; |
2897 |
codeLimit = (codeStart + codePtr->numCodeBytes); |
2898 |
numCmds = codePtr->numCommands; |
2899 |
|
2900 |
/* |
2901 |
* Print header lines describing the ByteCode. |
2902 |
*/ |
2903 |
|
2904 |
fprintf(stdout, "\nByteCode 0x%x, refCt %u, epoch %u, interp 0x%x (epoch %u)\n", |
2905 |
(unsigned int) codePtr, codePtr->refCount, |
2906 |
codePtr->compileEpoch, (unsigned int) iPtr, |
2907 |
iPtr->compileEpoch); |
2908 |
fprintf(stdout, " Source "); |
2909 |
TclPrintSource(stdout, codePtr->source, |
2910 |
TclMin(codePtr->numSrcBytes, 55)); |
2911 |
fprintf(stdout, "\n Cmds %d, src %d, inst %d, litObjs %u, aux %d, stkDepth %u, code/src %.2f\n", |
2912 |
numCmds, codePtr->numSrcBytes, codePtr->numCodeBytes, |
2913 |
codePtr->numLitObjects, codePtr->numAuxDataItems, |
2914 |
codePtr->maxStackDepth, |
2915 |
#ifdef TCL_COMPILE_STATS |
2916 |
(codePtr->numSrcBytes? |
2917 |
((float)codePtr->structureSize)/((float)codePtr->numSrcBytes) : 0.0)); |
2918 |
#else |
2919 |
0.0); |
2920 |
#endif |
2921 |
#ifdef TCL_COMPILE_STATS |
2922 |
fprintf(stdout, |
2923 |
" Code %d = header %d+inst %d+litObj %d+exc %d+aux %d+cmdMap %d\n", |
2924 |
codePtr->structureSize, |
2925 |
(sizeof(ByteCode) - (sizeof(size_t) + sizeof(Tcl_Time))), |
2926 |
codePtr->numCodeBytes, |
2927 |
(codePtr->numLitObjects * sizeof(Tcl_Obj *)), |
2928 |
(codePtr->numExceptRanges * sizeof(ExceptionRange)), |
2929 |
(codePtr->numAuxDataItems * sizeof(AuxData)), |
2930 |
codePtr->numCmdLocBytes); |
2931 |
#endif /* TCL_COMPILE_STATS */ |
2932 |
|
2933 |
/* |
2934 |
* If the ByteCode is the compiled body of a Tcl procedure, print |
2935 |
* information about that procedure. Note that we don't know the |
2936 |
* procedure's name since ByteCode's can be shared among procedures. |
2937 |
*/ |
2938 |
|
2939 |
if (codePtr->procPtr != NULL) { |
2940 |
Proc *procPtr = codePtr->procPtr; |
2941 |
int numCompiledLocals = procPtr->numCompiledLocals; |
2942 |
fprintf(stdout, |
2943 |
" Proc 0x%x, refCt %d, args %d, compiled locals %d\n", |
2944 |
(unsigned int) procPtr, procPtr->refCount, procPtr->numArgs, |
2945 |
numCompiledLocals); |
2946 |
if (numCompiledLocals > 0) { |
2947 |
CompiledLocal *localPtr = procPtr->firstLocalPtr; |
2948 |
for (i = 0; i < numCompiledLocals; i++) { |
2949 |
fprintf(stdout, " slot %d%s%s%s%s%s%s", i, |
2950 |
((localPtr->flags & VAR_SCALAR)? ", scalar" : ""), |
2951 |
((localPtr->flags & VAR_ARRAY)? ", array" : ""), |
2952 |
((localPtr->flags & VAR_LINK)? ", link" : ""), |
2953 |
((localPtr->flags & VAR_ARGUMENT)? ", arg" : ""), |
2954 |
((localPtr->flags & VAR_TEMPORARY)? ", temp" : ""), |
2955 |
((localPtr->flags & VAR_RESOLVED)? ", resolved" : "")); |
2956 |
if (TclIsVarTemporary(localPtr)) { |
2957 |
fprintf(stdout, "\n"); |
2958 |
} else { |
2959 |
fprintf(stdout, ", \"%s\"\n", localPtr->name); |
2960 |
} |
2961 |
localPtr = localPtr->nextPtr; |
2962 |
} |
2963 |
} |
2964 |
} |
2965 |
|
2966 |
/* |
2967 |
* Print the ExceptionRange array. |
2968 |
*/ |
2969 |
|
2970 |
if (codePtr->numExceptRanges > 0) { |
2971 |
fprintf(stdout, " Exception ranges %d, depth %d:\n", |
2972 |
codePtr->numExceptRanges, codePtr->maxExceptDepth); |
2973 |
for (i = 0; i < codePtr->numExceptRanges; i++) { |
2974 |
ExceptionRange *rangePtr = &(codePtr->exceptArrayPtr[i]); |
2975 |
fprintf(stdout, " %d: level %d, %s, pc %d-%d, ", |
2976 |
i, rangePtr->nestingLevel, |
2977 |
((rangePtr->type == LOOP_EXCEPTION_RANGE) |
2978 |
? "loop" : "catch"), |
2979 |
rangePtr->codeOffset, |
2980 |
(rangePtr->codeOffset + rangePtr->numCodeBytes - 1)); |
2981 |
switch (rangePtr->type) { |
2982 |
case LOOP_EXCEPTION_RANGE: |
2983 |
fprintf(stdout, "continue %d, break %d\n", |
2984 |
rangePtr->continueOffset, rangePtr->breakOffset); |
2985 |
break; |
2986 |
case CATCH_EXCEPTION_RANGE: |
2987 |
fprintf(stdout, "catch %d\n", rangePtr->catchOffset); |
2988 |
break; |
2989 |
default: |
2990 |
panic("TclPrintByteCodeObj: bad ExceptionRange type %d\n", |
2991 |
rangePtr->type); |
2992 |
} |
2993 |
} |
2994 |
} |
2995 |
|
2996 |
/* |
2997 |
* If there were no commands (e.g., an expression or an empty string |
2998 |
* was compiled), just print all instructions and return. |
2999 |
*/ |
3000 |
|
3001 |
if (numCmds == 0) { |
3002 |
pc = codeStart; |
3003 |
while (pc < codeLimit) { |
3004 |
fprintf(stdout, " "); |
3005 |
pc += TclPrintInstruction(codePtr, pc); |
3006 |
} |
3007 |
return; |
3008 |
} |
3009 |
|
3010 |
/* |
3011 |
* Print table showing the code offset, source offset, and source |
3012 |
* length for each command. These are encoded as a sequence of bytes. |
3013 |
*/ |
3014 |
|
3015 |
fprintf(stdout, " Commands %d:", numCmds); |
3016 |
codeDeltaNext = codePtr->codeDeltaStart; |
3017 |
codeLengthNext = codePtr->codeLengthStart; |
3018 |
srcDeltaNext = codePtr->srcDeltaStart; |
3019 |
srcLengthNext = codePtr->srcLengthStart; |
3020 |
codeOffset = srcOffset = 0; |
3021 |
for (i = 0; i < numCmds; i++) { |
3022 |
if ((unsigned int) (*codeDeltaNext) == (unsigned int) 0xFF) { |
3023 |
codeDeltaNext++; |
3024 |
delta = TclGetInt4AtPtr(codeDeltaNext); |
3025 |
codeDeltaNext += 4; |
3026 |
} else { |
3027 |
delta = TclGetInt1AtPtr(codeDeltaNext); |
3028 |
codeDeltaNext++; |
3029 |
} |
3030 |
codeOffset += delta; |
3031 |
|
3032 |
if ((unsigned int) (*codeLengthNext) == (unsigned int) 0xFF) { |
3033 |
codeLengthNext++; |
3034 |
codeLen = TclGetInt4AtPtr(codeLengthNext); |
3035 |
codeLengthNext += 4; |
3036 |
} else { |
3037 |
codeLen = TclGetInt1AtPtr(codeLengthNext); |
3038 |
codeLengthNext++; |
3039 |
} |
3040 |
|
3041 |
if ((unsigned int) (*srcDeltaNext) == (unsigned int) 0xFF) { |
3042 |
srcDeltaNext++; |
3043 |
delta = TclGetInt4AtPtr(srcDeltaNext); |
3044 |
srcDeltaNext += 4; |
3045 |
} else { |
3046 |
delta = TclGetInt1AtPtr(srcDeltaNext); |
3047 |
srcDeltaNext++; |
3048 |
} |
3049 |
srcOffset += delta; |
3050 |
|
3051 |
if ((unsigned int) (*srcLengthNext) == (unsigned int) 0xFF) { |
3052 |
srcLengthNext++; |
3053 |
srcLen = TclGetInt4AtPtr(srcLengthNext); |
3054 |
srcLengthNext += 4; |
3055 |
} else { |
3056 |
srcLen = TclGetInt1AtPtr(srcLengthNext); |
3057 |
srcLengthNext++; |
3058 |
} |
3059 |
|
3060 |
fprintf(stdout, "%s%4d: pc %d-%d, src %d-%d", |
3061 |
((i % 2)? " " : "\n "), |
3062 |
(i+1), codeOffset, (codeOffset + codeLen - 1), |
3063 |
srcOffset, (srcOffset + srcLen - 1)); |
3064 |
} |
3065 |
if (numCmds > 0) { |
3066 |
fprintf(stdout, "\n"); |
3067 |
} |
3068 |
|
3069 |
/* |
3070 |
* Print each instruction. If the instruction corresponds to the start |
3071 |
* of a command, print the command's source. Note that we don't need |
3072 |
* the code length here. |
3073 |
*/ |
3074 |
|
3075 |
codeDeltaNext = codePtr->codeDeltaStart; |
3076 |
srcDeltaNext = codePtr->srcDeltaStart; |
3077 |
srcLengthNext = codePtr->srcLengthStart; |
3078 |
codeOffset = srcOffset = 0; |
3079 |
pc = codeStart; |
3080 |
for (i = 0; i < numCmds; i++) { |
3081 |
if ((unsigned int) (*codeDeltaNext) == (unsigned int) 0xFF) { |
3082 |
codeDeltaNext++; |
3083 |
delta = TclGetInt4AtPtr(codeDeltaNext); |
3084 |
codeDeltaNext += 4; |
3085 |
} else { |
3086 |
delta = TclGetInt1AtPtr(codeDeltaNext); |
3087 |
codeDeltaNext++; |
3088 |
} |
3089 |
codeOffset += delta; |
3090 |
|
3091 |
if ((unsigned int) (*srcDeltaNext) == (unsigned int) 0xFF) { |
3092 |
srcDeltaNext++; |
3093 |
delta = TclGetInt4AtPtr(srcDeltaNext); |
3094 |
srcDeltaNext += 4; |
3095 |
} else { |
3096 |
delta = TclGetInt1AtPtr(srcDeltaNext); |
3097 |
srcDeltaNext++; |
3098 |
} |
3099 |
srcOffset += delta; |
3100 |
|
3101 |
if ((unsigned int) (*srcLengthNext) == (unsigned int) 0xFF) { |
3102 |
srcLengthNext++; |
3103 |
srcLen = TclGetInt4AtPtr(srcLengthNext); |
3104 |
srcLengthNext += 4; |
3105 |
} else { |
3106 |
srcLen = TclGetInt1AtPtr(srcLengthNext); |
3107 |
srcLengthNext++; |
3108 |
} |
3109 |
|
3110 |
/* |
3111 |
* Print instructions before command i. |
3112 |
*/ |
3113 |
|
3114 |
while ((pc-codeStart) < codeOffset) { |
3115 |
fprintf(stdout, " "); |
3116 |
pc += TclPrintInstruction(codePtr, pc); |
3117 |
} |
3118 |
|
3119 |
fprintf(stdout, " Command %d: ", (i+1)); |
3120 |
TclPrintSource(stdout, (codePtr->source + srcOffset), |
3121 |
TclMin(srcLen, 55)); |
3122 |
fprintf(stdout, "\n"); |
3123 |
} |
3124 |
if (pc < codeLimit) { |
3125 |
/* |
3126 |
* Print instructions after the last command. |
3127 |
*/ |
3128 |
|
3129 |
while (pc < codeLimit) { |
3130 |
fprintf(stdout, " "); |
3131 |
pc += TclPrintInstruction(codePtr, pc); |
3132 |
} |
3133 |
} |
3134 |
} |
3135 |
#endif /* TCL_COMPILE_DEBUG */ |
3136 |
|
3137 |
/* |
3138 |
*---------------------------------------------------------------------- |
3139 |
* |
3140 |
* TclPrintInstruction -- |
3141 |
* |
3142 |
* This procedure prints ("disassembles") one instruction from a |
3143 |
* bytecode object to stdout. |
3144 |
* |
3145 |
* Results: |
3146 |
* Returns the length in bytes of the current instruiction. |
3147 |
* |
3148 |
* Side effects: |
3149 |
* None. |
3150 |
* |
3151 |
*---------------------------------------------------------------------- |
3152 |
*/ |
3153 |
|
3154 |
int |
3155 |
TclPrintInstruction(codePtr, pc) |
3156 |
ByteCode* codePtr; /* Bytecode containing the instruction. */ |
3157 |
unsigned char *pc; /* Points to first byte of instruction. */ |
3158 |
{ |
3159 |
Proc *procPtr = codePtr->procPtr; |
3160 |
unsigned char opCode = *pc; |
3161 |
register InstructionDesc *instDesc = &instructionTable[opCode]; |
3162 |
unsigned char *codeStart = codePtr->codeStart; |
3163 |
unsigned int pcOffset = (pc - codeStart); |
3164 |
int opnd, i, j; |
3165 |
|
3166 |
fprintf(stdout, "(%u) %s ", pcOffset, instDesc->name); |
3167 |
for (i = 0; i < instDesc->numOperands; i++) { |
3168 |
switch (instDesc->opTypes[i]) { |
3169 |
case OPERAND_INT1: |
3170 |
opnd = TclGetInt1AtPtr(pc+1+i); |
3171 |
if ((i == 0) && ((opCode == INST_JUMP1) |
3172 |
|| (opCode == INST_JUMP_TRUE1) |
3173 |
|| (opCode == INST_JUMP_FALSE1))) { |
3174 |
fprintf(stdout, "%d # pc %u", opnd, (pcOffset + opnd)); |
3175 |
} else { |
3176 |
fprintf(stdout, "%d", opnd); |
3177 |
} |
3178 |
break; |
3179 |
case OPERAND_INT4: |
3180 |
opnd = TclGetInt4AtPtr(pc+1+i); |
3181 |
if ((i == 0) && ((opCode == INST_JUMP4) |
3182 |
|| (opCode == INST_JUMP_TRUE4) |
3183 |
|| (opCode == INST_JUMP_FALSE4))) { |
3184 |
fprintf(stdout, "%d # pc %u", opnd, (pcOffset + opnd)); |
3185 |
} else { |
3186 |
fprintf(stdout, "%d", opnd); |
3187 |
} |
3188 |
break; |
3189 |
case OPERAND_UINT1: |
3190 |
opnd = TclGetUInt1AtPtr(pc+1+i); |
3191 |
if ((i == 0) && (opCode == INST_PUSH1)) { |
3192 |
fprintf(stdout, "%u # ", (unsigned int) opnd); |
3193 |
TclPrintObject(stdout, codePtr->objArrayPtr[opnd], 40); |
3194 |
} else if ((i == 0) && ((opCode == INST_LOAD_SCALAR1) |
3195 |
|| (opCode == INST_LOAD_ARRAY1) |
3196 |
|| (opCode == INST_STORE_SCALAR1) |
3197 |
|| (opCode == INST_STORE_ARRAY1))) { |
3198 |
int localCt = procPtr->numCompiledLocals; |
3199 |
CompiledLocal *localPtr = procPtr->firstLocalPtr; |
3200 |
if (opnd >= localCt) { |
3201 |
panic("TclPrintInstruction: bad local var index %u (%u locals)\n", |
3202 |
(unsigned int) opnd, localCt); |
3203 |
return instDesc->numBytes; |
3204 |
} |
3205 |
for (j = 0; j < opnd; j++) { |
3206 |
localPtr = localPtr->nextPtr; |
3207 |
} |
3208 |
if (TclIsVarTemporary(localPtr)) { |
3209 |
fprintf(stdout, "%u # temp var %u", |
3210 |
(unsigned int) opnd, (unsigned int) opnd); |
3211 |
} else { |
3212 |
fprintf(stdout, "%u # var ", (unsigned int) opnd); |
3213 |
TclPrintSource(stdout, localPtr->name, 40); |
3214 |
} |
3215 |
} else { |
3216 |
fprintf(stdout, "%u ", (unsigned int) opnd); |
3217 |
} |
3218 |
break; |
3219 |
case OPERAND_UINT4: |
3220 |
opnd = TclGetUInt4AtPtr(pc+1+i); |
3221 |
if (opCode == INST_PUSH4) { |
3222 |
fprintf(stdout, "%u # ", opnd); |
3223 |
TclPrintObject(stdout, codePtr->objArrayPtr[opnd], 40); |
3224 |
} else if ((i == 0) && ((opCode == INST_LOAD_SCALAR4) |
3225 |
|| (opCode == INST_LOAD_ARRAY4) |
3226 |
|| (opCode == INST_STORE_SCALAR4) |
3227 |
|| (opCode == INST_STORE_ARRAY4))) { |
3228 |
int localCt = procPtr->numCompiledLocals; |
3229 |
CompiledLocal *localPtr = procPtr->firstLocalPtr; |
3230 |
if (opnd >= localCt) { |
3231 |
panic("TclPrintInstruction: bad local var index %u (%u locals)\n", |
3232 |
(unsigned int) opnd, localCt); |
3233 |
return instDesc->numBytes; |
3234 |
} |
3235 |
for (j = 0; j < opnd; j++) { |
3236 |
localPtr = localPtr->nextPtr; |
3237 |
} |
3238 |
if (TclIsVarTemporary(localPtr)) { |
3239 |
fprintf(stdout, "%u # temp var %u", |
3240 |
(unsigned int) opnd, (unsigned int) opnd); |
3241 |
} else { |
3242 |
fprintf(stdout, "%u # var ", (unsigned int) opnd); |
3243 |
TclPrintSource(stdout, localPtr->name, 40); |
3244 |
} |
3245 |
} else { |
3246 |
fprintf(stdout, "%u ", (unsigned int) opnd); |
3247 |
} |
3248 |
break; |
3249 |
case OPERAND_NONE: |
3250 |
default: |
3251 |
break; |
3252 |
} |
3253 |
} |
3254 |
fprintf(stdout, "\n"); |
3255 |
return instDesc->numBytes; |
3256 |
} |
3257 |
|
3258 |
/* |
3259 |
*---------------------------------------------------------------------- |
3260 |
* |
3261 |
* TclPrintObject -- |
3262 |
* |
3263 |
* This procedure prints up to a specified number of characters from |
3264 |
* the argument Tcl object's string representation to a specified file. |
3265 |
* |
3266 |
* Results: |
3267 |
* None. |
3268 |
* |
3269 |
* Side effects: |
3270 |
* Outputs characters to the specified file. |
3271 |
* |
3272 |
*---------------------------------------------------------------------- |
3273 |
*/ |
3274 |
|
3275 |
void |
3276 |
TclPrintObject(outFile, objPtr, maxChars) |
3277 |
FILE *outFile; /* The file to print the source to. */ |
3278 |
Tcl_Obj *objPtr; /* Points to the Tcl object whose string |
3279 |
* representation should be printed. */ |
3280 |
int maxChars; /* Maximum number of chars to print. */ |
3281 |
{ |
3282 |
char *bytes; |
3283 |
int length; |
3284 |
|
3285 |
bytes = Tcl_GetStringFromObj(objPtr, &length); |
3286 |
TclPrintSource(outFile, bytes, TclMin(length, maxChars)); |
3287 |
} |
3288 |
|
3289 |
/* |
3290 |
*---------------------------------------------------------------------- |
3291 |
* |
3292 |
* TclPrintSource -- |
3293 |
* |
3294 |
* This procedure prints up to a specified number of characters from |
3295 |
* the argument string to a specified file. It tries to produce legible |
3296 |
* output by adding backslashes as necessary. |
3297 |
* |
3298 |
* Results: |
3299 |
* None. |
3300 |
* |
3301 |
* Side effects: |
3302 |
* Outputs characters to the specified file. |
3303 |
* |
3304 |
*---------------------------------------------------------------------- |
3305 |
*/ |
3306 |
|
3307 |
void |
3308 |
TclPrintSource(outFile, string, maxChars) |
3309 |
FILE *outFile; /* The file to print the source to. */ |
3310 |
char *string; /* The string to print. */ |
3311 |
int maxChars; /* Maximum number of chars to print. */ |
3312 |
{ |
3313 |
register char *p; |
3314 |
register int i = 0; |
3315 |
|
3316 |
if (string == NULL) { |
3317 |
fprintf(outFile, "\"\""); |
3318 |
return; |
3319 |
} |
3320 |
|
3321 |
fprintf(outFile, "\""); |
3322 |
p = string; |
3323 |
for (; (*p != '\0') && (i < maxChars); p++, i++) { |
3324 |
switch (*p) { |
3325 |
case '"': |
3326 |
fprintf(outFile, "\\\""); |
3327 |
continue; |
3328 |
case '\f': |
3329 |
fprintf(outFile, "\\f"); |
3330 |
continue; |
3331 |
case '\n': |
3332 |
fprintf(outFile, "\\n"); |
3333 |
continue; |
3334 |
case '\r': |
3335 |
fprintf(outFile, "\\r"); |
3336 |
continue; |
3337 |
case '\t': |
3338 |
fprintf(outFile, "\\t"); |
3339 |
continue; |
3340 |
case '\v': |
3341 |
fprintf(outFile, "\\v"); |
3342 |
continue; |
3343 |
default: |
3344 |
fprintf(outFile, "%c", *p); |
3345 |
continue; |
3346 |
} |
3347 |
} |
3348 |
fprintf(outFile, "\""); |
3349 |
} |
3350 |
|
3351 |
#ifdef TCL_COMPILE_STATS |
3352 |
/* |
3353 |
*---------------------------------------------------------------------- |
3354 |
* |
3355 |
* RecordByteCodeStats -- |
3356 |
* |
3357 |
* Accumulates various compilation-related statistics for each newly |
3358 |
* compiled ByteCode. Called by the TclInitByteCodeObj when Tcl is |
3359 |
* compiled with the -DTCL_COMPILE_STATS flag |
3360 |
* |
3361 |
* Results: |
3362 |
* None. |
3363 |
* |
3364 |
* Side effects: |
3365 |
* Accumulates aggregate code-related statistics in the interpreter's |
3366 |
* ByteCodeStats structure. Records statistics specific to a ByteCode |
3367 |
* in its ByteCode structure. |
3368 |
* |
3369 |
*---------------------------------------------------------------------- |
3370 |
*/ |
3371 |
|
3372 |
void |
3373 |
RecordByteCodeStats(codePtr) |
3374 |
ByteCode *codePtr; /* Points to ByteCode structure with info |
3375 |
* to add to accumulated statistics. */ |
3376 |
{ |
3377 |
Interp *iPtr = (Interp *) *codePtr->interpHandle; |
3378 |
register ByteCodeStats *statsPtr = &(iPtr->stats); |
3379 |
|
3380 |
statsPtr->numCompilations++; |
3381 |
statsPtr->totalSrcBytes += (double) codePtr->numSrcBytes; |
3382 |
statsPtr->totalByteCodeBytes += (double) codePtr->structureSize; |
3383 |
statsPtr->currentSrcBytes += (double) codePtr->numSrcBytes; |
3384 |
statsPtr->currentByteCodeBytes += (double) codePtr->structureSize; |
3385 |
|
3386 |
statsPtr->srcCount[TclLog2(codePtr->numSrcBytes)]++; |
3387 |
statsPtr->byteCodeCount[TclLog2(codePtr->structureSize)]++; |
3388 |
|
3389 |
statsPtr->currentInstBytes += (double) codePtr->numCodeBytes; |
3390 |
statsPtr->currentLitBytes += |
3391 |
(double) (codePtr->numLitObjects * sizeof(Tcl_Obj *)); |
3392 |
statsPtr->currentExceptBytes += |
3393 |
(double) (codePtr->numExceptRanges * sizeof(ExceptionRange)); |
3394 |
statsPtr->currentAuxBytes += |
3395 |
(double) (codePtr->numAuxDataItems * sizeof(AuxData)); |
3396 |
statsPtr->currentCmdMapBytes += (double) codePtr->numCmdLocBytes; |
3397 |
} |
3398 |
#endif /* TCL_COMPILE_STATS */ |
3399 |
|
3400 |
/* End of tclcompile.c */ |