Tcl Source Code

Check-in [0db7a63c31]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Fix problems with break and continue in for-step clauses, triggered by reporting of Coverity CID 1251203. Some of these were originally bugs in Tcl 8.0.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 0db7a63c31f78537c2da67872f28dbe17c7d167c
User & Date: dkf 2015-08-03 07:21:29
Context
2015-08-03
09:57
merge mark check-in: 2c8fc45c70 user: dkf tags: trunk
09:55
merge mark Closed-Leaf check-in: b28720a3d7 user: dkf tags: mistake
07:21
Fix problems with break and continue in for-step clauses, triggered by reporting of Coverity CID 125... check-in: 0db7a63c31 user: dkf tags: trunk
07:18
Fix the documentation comment. Closed-Leaf check-in: 77a5609058 user: dkf tags: Coverity CID 1251203
2015-08-01
19:00
explicitly ignore Tcl_PushCallFrame and TclPushStackFrame return value - it is always TCL_OK. Incons... check-in: 7448dc7ef9 user: msofer tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to generic/tclCompile.c.

4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103











4104
4105
4106
4107
4108
4109
4110
4111
     * special handling exception range (so that we can correctly unwind the
     * stack).
     *
     * These must be done separately; they can be different (especially for
     * calls from inside a [for] increment clause).
     */

    rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_BREAK, &auxBreakPtr);
    if (rangePtr == NULL || rangePtr->type != LOOP_EXCEPTION_RANGE) {
	auxBreakPtr = NULL;
    } else if (auxBreakPtr->stackDepth == envPtr->currStackDepth-wordCount
	    && auxBreakPtr->expandTarget == envPtr->expandCount-expandCount) {
	auxBreakPtr = NULL;
    } else {
	breakRange = auxBreakPtr - envPtr->exceptAuxArrayPtr;
    }

    rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_CONTINUE,
	    &auxContinuePtr);
    if (rangePtr == NULL || rangePtr->type != LOOP_EXCEPTION_RANGE) {
	auxContinuePtr = NULL;
    } else if (auxContinuePtr->stackDepth == envPtr->currStackDepth-wordCount
	    && auxContinuePtr->expandTarget == envPtr->expandCount-expandCount) {
	auxContinuePtr = NULL;
    } else {











	continueRange = auxBreakPtr - envPtr->exceptAuxArrayPtr;
    }

    if (auxBreakPtr != NULL || auxContinuePtr != NULL) {
	loopRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
	ExceptionRangeStarts(envPtr, loopRange);
    }








<
<
<
<
<
<
<
<
<
<








>
>
>
>
>
>
>
>
>
>
>
|







4079
4080
4081
4082
4083
4084
4085










4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110
4111
4112
     * special handling exception range (so that we can correctly unwind the
     * stack).
     *
     * These must be done separately; they can be different (especially for
     * calls from inside a [for] increment clause).
     */











    rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_CONTINUE,
	    &auxContinuePtr);
    if (rangePtr == NULL || rangePtr->type != LOOP_EXCEPTION_RANGE) {
	auxContinuePtr = NULL;
    } else if (auxContinuePtr->stackDepth == envPtr->currStackDepth-wordCount
	    && auxContinuePtr->expandTarget == envPtr->expandCount-expandCount) {
	auxContinuePtr = NULL;
    } else {
	continueRange = auxContinuePtr - envPtr->exceptAuxArrayPtr;
    }

    rangePtr = TclGetInnermostExceptionRange(envPtr, TCL_BREAK, &auxBreakPtr);
    if (rangePtr == NULL || rangePtr->type != LOOP_EXCEPTION_RANGE) {
	auxBreakPtr = NULL;
    } else if (auxContinuePtr == NULL
	    && auxBreakPtr->stackDepth == envPtr->currStackDepth-wordCount
	    && auxBreakPtr->expandTarget == envPtr->expandCount-expandCount) {
	auxBreakPtr = NULL;
    } else {
	breakRange = auxBreakPtr - envPtr->exceptAuxArrayPtr;
    }

    if (auxBreakPtr != NULL || auxContinuePtr != NULL) {
	loopRange = TclCreateExceptRange(LOOP_EXCEPTION_RANGE, envPtr);
	ExceptionRangeStarts(envPtr, loopRange);
    }

Changes to generic/tclExecute.c.

754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
static Tcl_Obj *	ExecuteExtendedBinaryMathOp(Tcl_Interp *interp,
			    int opcode, Tcl_Obj **constants,
			    Tcl_Obj *valuePtr, Tcl_Obj *value2Ptr);
static Tcl_Obj *	ExecuteExtendedUnaryMathOp(int opcode,
			    Tcl_Obj *valuePtr);
static void		FreeExprCodeInternalRep(Tcl_Obj *objPtr);
static ExceptionRange *	GetExceptRangeForPc(const unsigned char *pc,
			    int catchOnly, ByteCode *codePtr);
static const char *	GetSrcInfoForPc(const unsigned char *pc,
			    ByteCode *codePtr, int *lengthPtr,
			    const unsigned char **pcBeg, int *cmdIdxPtr);
static Tcl_Obj **	GrowEvaluationStack(ExecEnv *eePtr, int growth,
			    int move);
static void		IllegalExprOperandType(Tcl_Interp *interp,
			    const unsigned char *pc, Tcl_Obj *opndPtr);







|







754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
static Tcl_Obj *	ExecuteExtendedBinaryMathOp(Tcl_Interp *interp,
			    int opcode, Tcl_Obj **constants,
			    Tcl_Obj *valuePtr, Tcl_Obj *value2Ptr);
static Tcl_Obj *	ExecuteExtendedUnaryMathOp(int opcode,
			    Tcl_Obj *valuePtr);
static void		FreeExprCodeInternalRep(Tcl_Obj *objPtr);
static ExceptionRange *	GetExceptRangeForPc(const unsigned char *pc,
			    int searchMode, ByteCode *codePtr);
static const char *	GetSrcInfoForPc(const unsigned char *pc,
			    ByteCode *codePtr, int *lengthPtr,
			    const unsigned char **pcBeg, int *cmdIdxPtr);
static Tcl_Obj **	GrowEvaluationStack(ExecEnv *eePtr, int growth,
			    int move);
static void		IllegalExprOperandType(Tcl_Interp *interp,
			    const unsigned char *pc, Tcl_Obj *opndPtr);
7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
	    TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS)));
	    break;
	default:
	    TRACE(("=> "));
	}
#endif
	if ((result == TCL_CONTINUE) || (result == TCL_BREAK)) {
	    rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 0, codePtr);
	    if (rangePtr == NULL) {
		TRACE_APPEND(("no encl. loop or catch, returning %s\n",
			StringForResultCode(result)));
		goto abnormalReturn;
	    }
	    if (rangePtr->type == CATCH_EXCEPTION_RANGE) {
		TRACE_APPEND(("%s ...\n", StringForResultCode(result)));







|







7951
7952
7953
7954
7955
7956
7957
7958
7959
7960
7961
7962
7963
7964
7965
	    TRACE(("\"%.30s\" => ", O2S(OBJ_AT_TOS)));
	    break;
	default:
	    TRACE(("=> "));
	}
#endif
	if ((result == TCL_CONTINUE) || (result == TCL_BREAK)) {
	    rangePtr = GetExceptRangeForPc(pc, result, codePtr);
	    if (rangePtr == NULL) {
		TRACE_APPEND(("no encl. loop or catch, returning %s\n",
			StringForResultCode(result)));
		goto abnormalReturn;
	    }
	    if (rangePtr->type == CATCH_EXCEPTION_RANGE) {
		TRACE_APPEND(("%s ...\n", StringForResultCode(result)));
8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
	    if (traceInstructions) {
		fprintf(stdout, "   ... no enclosing catch, returning %s\n",
			StringForResultCode(result));
	    }
#endif
	    goto abnormalReturn;
	}
	rangePtr = GetExceptRangeForPc(pc, /*catchOnly*/ 1, codePtr);
	if (rangePtr == NULL) {
	    /*
	     * This is only possible when compiling a [catch] that sends its
	     * script to INST_EVAL. Cannot correct the compiler without
	     * breaking compat with previous .tbc compiled scripts.
	     */








|







8112
8113
8114
8115
8116
8117
8118
8119
8120
8121
8122
8123
8124
8125
8126
	    if (traceInstructions) {
		fprintf(stdout, "   ... no enclosing catch, returning %s\n",
			StringForResultCode(result));
	    }
#endif
	    goto abnormalReturn;
	}
	rangePtr = GetExceptRangeForPc(pc, TCL_ERROR, codePtr);
	if (rangePtr == NULL) {
	    /*
	     * This is only possible when compiling a [catch] that sends its
	     * script to INST_EVAL. Cannot correct the compiler without
	     * breaking compat with previous .tbc compiled scripts.
	     */

10108
10109
10110
10111
10112
10113
10114


10115
10116
10117
10118
10119

10120
10121
10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139


10140
10141
10142
10143
10144
10145
10146
 *
 * GetExceptRangeForPc --
 *
 *	Given a program counter value, return the closest enclosing
 *	ExceptionRange.
 *
 * Results:


 *	In the normal case, catchOnly is 0 (false) and this procedure returns
 *	a pointer to the most closely enclosing ExceptionRange structure
 *	regardless of whether it is a loop or catch exception range. This is
 *	appropriate when processing a TCL_BREAK or TCL_CONTINUE, which will be
 *	"handled" either by a loop exception range or a closer catch range. If

 *	catchOnly is nonzero, this procedure ignores loop exception ranges and
 *	returns a pointer to the closest catch range. If no matching
 *	ExceptionRange is found that encloses pc, a NULL is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static ExceptionRange *
GetExceptRangeForPc(
    const unsigned char *pc, /* The program counter value for which to
				 * search for a closest enclosing exception
				 * range. This points to a bytecode
				 * instruction in codePtr's code. */
    int catchOnly,		/* If 0, consider either loop or catch
				 * ExceptionRanges in search. If nonzero
				 * consider only catch ranges (and ignore any
				 * closer loop ranges). */


    ByteCode *codePtr)		/* Points to the ByteCode in which to search
				 * for the enclosing ExceptionRange. */
{
    ExceptionRange *rangeArrayPtr;
    int numRanges = codePtr->numExceptRanges;
    register ExceptionRange *rangePtr;
    int pcOffset = pc - codePtr->codeStart;







>
>
|
|
|
<
<
>
|
|










|



|
|

|
>
>







10108
10109
10110
10111
10112
10113
10114
10115
10116
10117
10118
10119


10120
10121
10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142
10143
10144
10145
10146
10147
10148
10149
 *
 * GetExceptRangeForPc --
 *
 *	Given a program counter value, return the closest enclosing
 *	ExceptionRange.
 *
 * Results:
 *	If the searchMode is TCL_ERROR, this procedure ignores loop exception
 *	ranges and returns a pointer to the closest catch range. If the
 *	searchMode is TCL_BREAK, this procedure returns a pointer to the most
 *	closely enclosing ExceptionRange regardless of whether it is a loop or
 *	catch exception range. If the searchMode is TCL_CONTINUE, this


 *	procedure returns a pointer to the most closely enclosing
 *	ExceptionRange (of any type) skipping only loop exception ranges if
 *	they don't have a sensible continueOffset defined. If no matching
 *	ExceptionRange is found that encloses pc, a NULL is returned.
 *
 * Side effects:
 *	None.
 *
 *----------------------------------------------------------------------
 */

static ExceptionRange *
GetExceptRangeForPc(
    const unsigned char *pc,	/* The program counter value for which to
				 * search for a closest enclosing exception
				 * range. This points to a bytecode
				 * instruction in codePtr's code. */
    int searchMode,		/* If TCL_BREAK, consider either loop or catch
				 * ExceptionRanges in search. If TCL_ERROR
				 * consider only catch ranges (and ignore any
				 * closer loop ranges). If TCL_CONTINUE, look
				 * for loop ranges that define a continue
				 * point or a catch range. */
    ByteCode *codePtr)		/* Points to the ByteCode in which to search
				 * for the enclosing ExceptionRange. */
{
    ExceptionRange *rangeArrayPtr;
    int numRanges = codePtr->numExceptRanges;
    register ExceptionRange *rangePtr;
    int pcOffset = pc - codePtr->codeStart;
10158
10159
10160
10161
10162
10163
10164
10165
10166






10167
10168
10169
10170
10171
10172
10173

    rangeArrayPtr = codePtr->exceptArrayPtr;
    rangePtr = rangeArrayPtr + numRanges;
    while (--rangePtr >= rangeArrayPtr) {
	start = rangePtr->codeOffset;
	if ((start <= pcOffset) &&
		(pcOffset < (start + rangePtr->numCodeBytes))) {
	    if ((!catchOnly)
		    || (rangePtr->type == CATCH_EXCEPTION_RANGE)) {






		return rangePtr;
	    }
	}
    }
    return NULL;
}








<
|
>
>
>
>
>
>







10161
10162
10163
10164
10165
10166
10167

10168
10169
10170
10171
10172
10173
10174
10175
10176
10177
10178
10179
10180
10181

    rangeArrayPtr = codePtr->exceptArrayPtr;
    rangePtr = rangeArrayPtr + numRanges;
    while (--rangePtr >= rangeArrayPtr) {
	start = rangePtr->codeOffset;
	if ((start <= pcOffset) &&
		(pcOffset < (start + rangePtr->numCodeBytes))) {

	    if (rangePtr->type == CATCH_EXCEPTION_RANGE) {
		return rangePtr;
	    }
	    if (searchMode == TCL_BREAK) {
		return rangePtr;
	    }
	    if (searchMode == TCL_CONTINUE && rangePtr->continueOffset != -1){
		return rangePtr;
	    }
	}
    }
    return NULL;
}


Changes to tests/for.test.

1180
1181
1182
1183
1184
1185
1186






































































































































































1187
1188
1189
1190
1191
1192
1193
	    }]
	    set tmp $end
	    set end [meminfo]
	}
	expr {$end - $tmp}
    }} {return -level 0 -code continue}
} 0







































































































































































# cleanup
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
	    }]
	    set tmp $end
	    set end [meminfo]
	}
	expr {$end - $tmp}
    }} {return -level 0 -code continue}
} 0

test for-8.0 {Coverity CID 1251203: break vs continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i; list a [eval {}]} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {6 5 3}
test for-8.1 {Coverity CID 1251203: break vs continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i;list a [eval break]} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {2 1 3}
test for-8.2 {Coverity CID 1251203: break vs continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i;list a [eval continue]} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {1 1 3}
test for-8.3 {break in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i; break} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {2 1 3}
test for-8.4 {continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i; continue} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {1 1 3}
test for-8.5 {break in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i; list a [break]} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {2 1 3}
test for-8.6 {continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i; list a [continue]} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {1 1 3}
test for-8.7 {break in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i;eval break} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {2 1 3}
test for-8.8 {continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    list a [\
	    for {set i 0} {$i < 5} {incr i;eval continue} {
		incr j
	    }]
	    incr i
	}
	list $i $j $k
    }}
} {1 1 3}
test for-8.9 {break in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    for {set i 0} {$i < 5} {incr i;eval break} {
		incr j
	    }
	    incr i
	}
	list $i $j $k
    }}
} {2 1 3}
test for-8.10 {continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    for {set i 0} {$i < 5} {incr i;eval continue} {
		incr j
	    }
	    incr i
	}
	list $i $j $k
    }}
} {1 1 3}
test for-8.11 {break in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    for {set i 0} {$i < 5} {incr i;break} {
		incr j
	    }
	    incr i
	}
	list $i $j $k
    }}
} {2 1 3}
test for-8.12 {continue in for-step clause} {
    apply {{} {
	for {set k 0} {$k < 3} {incr k} {
	    set j 0
	    for {set i 0} {$i < 5} {incr i;continue} {
		incr j
	    }
	    incr i
	}
	list $i $j $k
    }}
} {1 1 3}

# cleanup
::tcltest::cleanupTests
return

# Local Variables:
# mode: tcl