Fossil SCM
Fix another possible memory leak in a corner case. Add more tests. Improve comments on the new Find structure.
Commit
9b3b1d0714b1bd04c1b846878bc7b017ddbc1ed6
Parent
e4047acb76d90c6…
2 files changed
+21
-8
+20
M
src/th.c
+21
-8
| --- src/th.c | ||
| +++ src/th.c | ||
| @@ -1050,10 +1050,26 @@ | ||
| 1050 | 1050 | *pnInner = nInner; |
| 1051 | 1051 | *pisGlobal = isGlobal; |
| 1052 | 1052 | return TH_OK; |
| 1053 | 1053 | } |
| 1054 | 1054 | |
| 1055 | +/* | |
| 1056 | +** The Find structure is used to return extra information to callers of the | |
| 1057 | +** thFindValue function. The fields within it are populated by thFindValue | |
| 1058 | +** as soon as the necessary information is available. Callers should zero | |
| 1059 | +** out the structure prior to calling thFindValue and then check each field | |
| 1060 | +** of interest upon return. | |
| 1061 | +*/ | |
| 1062 | + | |
| 1063 | +struct Find { | |
| 1064 | + Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */ | |
| 1065 | + Th_HashEntry *pElemEntry; /* Pointer to the element hash entry, if any */ | |
| 1066 | + const char *zElem; /* Name of array element, if applicable */ | |
| 1067 | + int nElem; /* Length of array element name, if applicable */ | |
| 1068 | +}; | |
| 1069 | +typedef struct Find Find; | |
| 1070 | + | |
| 1055 | 1071 | /* |
| 1056 | 1072 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1057 | 1073 | ** the Th_Variable structure associated with the named variable. The |
| 1058 | 1074 | ** variable name may be a global or local scalar or array variable |
| 1059 | 1075 | ** |
| @@ -1063,17 +1079,10 @@ | ||
| 1063 | 1079 | ** |
| 1064 | 1080 | ** If the arrayok argument is false and the named variable is an array, |
| 1065 | 1081 | ** an error is left in the interpreter result and NULL returned. If |
| 1066 | 1082 | ** arrayok is true an array name is Ok. |
| 1067 | 1083 | */ |
| 1068 | -struct Find { | |
| 1069 | - Th_HashEntry *pValueEntry; | |
| 1070 | - Th_HashEntry *pElemEntry; | |
| 1071 | - const char *zElem; | |
| 1072 | - int nElem; | |
| 1073 | -}; | |
| 1074 | -typedef struct Find Find; | |
| 1075 | 1084 | |
| 1076 | 1085 | static Th_Variable *thFindValue( |
| 1077 | 1086 | Th_Interp *interp, |
| 1078 | 1087 | const char *zVar, /* Pointer to variable name */ |
| 1079 | 1088 | int nVar, /* Number of bytes at nVar */ |
| @@ -1310,11 +1319,11 @@ | ||
| 1310 | 1319 | assert( pValue ); |
| 1311 | 1320 | if( thFreeVariable(pEntry, (void *)interp) ){ |
| 1312 | 1321 | if( find.zElem ){ |
| 1313 | 1322 | Th_Variable *pValue2 = find.pValueEntry->pData; |
| 1314 | 1323 | Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); |
| 1315 | - }else{ | |
| 1324 | + }else if( pEntry->pData ){ | |
| 1316 | 1325 | Th_Free(interp, pEntry->pData); |
| 1317 | 1326 | pEntry->pData = 0; |
| 1318 | 1327 | } |
| 1319 | 1328 | }else{ |
| 1320 | 1329 | if( pValue->zData ){ |
| @@ -1324,10 +1333,14 @@ | ||
| 1324 | 1333 | if( pValue->pHash ){ |
| 1325 | 1334 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp); |
| 1326 | 1335 | Th_HashDelete(interp, pValue->pHash); |
| 1327 | 1336 | pValue->pHash = 0; |
| 1328 | 1337 | } |
| 1338 | + if( find.zElem ){ | |
| 1339 | + Th_Variable *pValue2 = find.pValueEntry->pData; | |
| 1340 | + Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); | |
| 1341 | + } | |
| 1329 | 1342 | } |
| 1330 | 1343 | if( !find.zElem ){ |
| 1331 | 1344 | thFindValue(interp, zVar, nVar, -1, 1, 1, 0); |
| 1332 | 1345 | } |
| 1333 | 1346 | return rc; |
| 1334 | 1347 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1050,10 +1050,26 @@ | |
| 1050 | *pnInner = nInner; |
| 1051 | *pisGlobal = isGlobal; |
| 1052 | return TH_OK; |
| 1053 | } |
| 1054 | |
| 1055 | /* |
| 1056 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1057 | ** the Th_Variable structure associated with the named variable. The |
| 1058 | ** variable name may be a global or local scalar or array variable |
| 1059 | ** |
| @@ -1063,17 +1079,10 @@ | |
| 1063 | ** |
| 1064 | ** If the arrayok argument is false and the named variable is an array, |
| 1065 | ** an error is left in the interpreter result and NULL returned. If |
| 1066 | ** arrayok is true an array name is Ok. |
| 1067 | */ |
| 1068 | struct Find { |
| 1069 | Th_HashEntry *pValueEntry; |
| 1070 | Th_HashEntry *pElemEntry; |
| 1071 | const char *zElem; |
| 1072 | int nElem; |
| 1073 | }; |
| 1074 | typedef struct Find Find; |
| 1075 | |
| 1076 | static Th_Variable *thFindValue( |
| 1077 | Th_Interp *interp, |
| 1078 | const char *zVar, /* Pointer to variable name */ |
| 1079 | int nVar, /* Number of bytes at nVar */ |
| @@ -1310,11 +1319,11 @@ | |
| 1310 | assert( pValue ); |
| 1311 | if( thFreeVariable(pEntry, (void *)interp) ){ |
| 1312 | if( find.zElem ){ |
| 1313 | Th_Variable *pValue2 = find.pValueEntry->pData; |
| 1314 | Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); |
| 1315 | }else{ |
| 1316 | Th_Free(interp, pEntry->pData); |
| 1317 | pEntry->pData = 0; |
| 1318 | } |
| 1319 | }else{ |
| 1320 | if( pValue->zData ){ |
| @@ -1324,10 +1333,14 @@ | |
| 1324 | if( pValue->pHash ){ |
| 1325 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp); |
| 1326 | Th_HashDelete(interp, pValue->pHash); |
| 1327 | pValue->pHash = 0; |
| 1328 | } |
| 1329 | } |
| 1330 | if( !find.zElem ){ |
| 1331 | thFindValue(interp, zVar, nVar, -1, 1, 1, 0); |
| 1332 | } |
| 1333 | return rc; |
| 1334 |
| --- src/th.c | |
| +++ src/th.c | |
| @@ -1050,10 +1050,26 @@ | |
| 1050 | *pnInner = nInner; |
| 1051 | *pisGlobal = isGlobal; |
| 1052 | return TH_OK; |
| 1053 | } |
| 1054 | |
| 1055 | /* |
| 1056 | ** The Find structure is used to return extra information to callers of the |
| 1057 | ** thFindValue function. The fields within it are populated by thFindValue |
| 1058 | ** as soon as the necessary information is available. Callers should zero |
| 1059 | ** out the structure prior to calling thFindValue and then check each field |
| 1060 | ** of interest upon return. |
| 1061 | */ |
| 1062 | |
| 1063 | struct Find { |
| 1064 | Th_HashEntry *pValueEntry; /* Pointer to the scalar or array hash entry */ |
| 1065 | Th_HashEntry *pElemEntry; /* Pointer to the element hash entry, if any */ |
| 1066 | const char *zElem; /* Name of array element, if applicable */ |
| 1067 | int nElem; /* Length of array element name, if applicable */ |
| 1068 | }; |
| 1069 | typedef struct Find Find; |
| 1070 | |
| 1071 | /* |
| 1072 | ** Input string (zVar, nVar) contains a variable name. This function locates |
| 1073 | ** the Th_Variable structure associated with the named variable. The |
| 1074 | ** variable name may be a global or local scalar or array variable |
| 1075 | ** |
| @@ -1063,17 +1079,10 @@ | |
| 1079 | ** |
| 1080 | ** If the arrayok argument is false and the named variable is an array, |
| 1081 | ** an error is left in the interpreter result and NULL returned. If |
| 1082 | ** arrayok is true an array name is Ok. |
| 1083 | */ |
| 1084 | |
| 1085 | static Th_Variable *thFindValue( |
| 1086 | Th_Interp *interp, |
| 1087 | const char *zVar, /* Pointer to variable name */ |
| 1088 | int nVar, /* Number of bytes at nVar */ |
| @@ -1310,11 +1319,11 @@ | |
| 1319 | assert( pValue ); |
| 1320 | if( thFreeVariable(pEntry, (void *)interp) ){ |
| 1321 | if( find.zElem ){ |
| 1322 | Th_Variable *pValue2 = find.pValueEntry->pData; |
| 1323 | Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); |
| 1324 | }else if( pEntry->pData ){ |
| 1325 | Th_Free(interp, pEntry->pData); |
| 1326 | pEntry->pData = 0; |
| 1327 | } |
| 1328 | }else{ |
| 1329 | if( pValue->zData ){ |
| @@ -1324,10 +1333,14 @@ | |
| 1333 | if( pValue->pHash ){ |
| 1334 | Th_HashIterate(interp, pValue->pHash, thFreeVariable, (void *)interp); |
| 1335 | Th_HashDelete(interp, pValue->pHash); |
| 1336 | pValue->pHash = 0; |
| 1337 | } |
| 1338 | if( find.zElem ){ |
| 1339 | Th_Variable *pValue2 = find.pValueEntry->pData; |
| 1340 | Th_HashFind(interp, pValue2->pHash, find.zElem, find.nElem, -1); |
| 1341 | } |
| 1342 | } |
| 1343 | if( !find.zElem ){ |
| 1344 | thFindValue(interp, zVar, nVar, -1, 1, 1, 0); |
| 1345 | } |
| 1346 | return rc; |
| 1347 |
+20
| --- test/th1.test | ||
| +++ test/th1.test | ||
| @@ -187,5 +187,25 @@ | ||
| 187 | 187 | |
| 188 | 188 | ############################################################################### |
| 189 | 189 | |
| 190 | 190 | fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv" |
| 191 | 191 | test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}} |
| 192 | + | |
| 193 | +############################################################################### | |
| 194 | + | |
| 195 | +fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2" | |
| 196 | +test th1-unset-5 {$RESULT eq {1}} | |
| 197 | + | |
| 198 | +############################################################################### | |
| 199 | + | |
| 200 | +fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2" | |
| 201 | +test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}} | |
| 202 | + | |
| 203 | +############################################################################### | |
| 204 | + | |
| 205 | +fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)" | |
| 206 | +test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}} | |
| 207 | + | |
| 208 | +############################################################################### | |
| 209 | + | |
| 210 | +fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2" | |
| 211 | +test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}} | |
| 192 | 212 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -187,5 +187,25 @@ | |
| 187 | |
| 188 | ############################################################################### |
| 189 | |
| 190 | fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv" |
| 191 | test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}} |
| 192 |
| --- test/th1.test | |
| +++ test/th1.test | |
| @@ -187,5 +187,25 @@ | |
| 187 | |
| 188 | ############################################################################### |
| 189 | |
| 190 | fossil test-th-eval "set gv 1; proc p {} {upvar 1 gv lv; unset lv}; p; unset gv" |
| 191 | test th1-unset-4 {$RESULT eq {TH_ERROR: no such variable: gv}} |
| 192 | |
| 193 | ############################################################################### |
| 194 | |
| 195 | fossil test-th-eval "set gv 1; upvar 0 gv gv2; info exists gv2" |
| 196 | test th1-unset-5 {$RESULT eq {1}} |
| 197 | |
| 198 | ############################################################################### |
| 199 | |
| 200 | fossil test-th-eval "set gv 1; upvar 0 gv gv2; unset gv; unset gv2" |
| 201 | test th1-unset-6 {$RESULT eq {TH_ERROR: no such variable: gv2}} |
| 202 | |
| 203 | ############################################################################### |
| 204 | |
| 205 | fossil test-th-eval "set gv 1; upvar 0 gv gv2(1); unset gv; unset gv2(1)" |
| 206 | test th1-unset-7 {$RESULT eq {TH_ERROR: no such variable: gv2(1)}} |
| 207 | |
| 208 | ############################################################################### |
| 209 | |
| 210 | fossil test-th-eval "set gv(1) 1; upvar 0 gv(1) gv2; unset gv(1); unset gv2" |
| 211 | test th1-unset-8 {$RESULT eq {TH_ERROR: no such variable: gv2}} |
| 212 |