Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Fix for issue 9fd5c629c1, TclOO - aborts when a trace on command deletion deletes the object's namespace. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | bug-fc1409fc91 |
Files: | files | file ages | folders |
SHA3-256: |
bee7f97ad6521604c0e4ff018e6c95c9 |
User & Date: | pooryorick 2017-10-30 05:19:55.879 |
Context
2018-02-14
| ||
10:46 | Fix for issue 9fd5c629c1, TclOO - aborts when a trace on command deletion deletes the object's names... check-in: 77641f0360 user: pooryorick | |
2017-10-30
| ||
05:25 | merge bug-fc1409fc91. check-in: 76eaf9a16b user: pooryorick tags: trunk | |
05:19 | Fix for issue 9fd5c629c1, TclOO - aborts when a trace on command deletion deletes the object's names... Closed-Leaf check-in: bee7f97ad6 user: pooryorick tags: bug-fc1409fc91 | |
2017-10-24
| ||
11:23 | Added test case. check-in: efdb206a73 user: dkf tags: bug-fc1409fc91 | |
Changes
Changes to generic/tclBasic.c.
︙ | ︙ | |||
3108 3109 3110 3111 3112 3113 3114 | return 0; } /* * We must delete this command, even though both traces and delete procs * may try to avoid this (renaming the command etc). Also traces and | | | 3108 3109 3110 3111 3112 3113 3114 3115 3116 3117 3118 3119 3120 3121 3122 | return 0; } /* * We must delete this command, even though both traces and delete procs * may try to avoid this (renaming the command etc). Also traces and * delete procs may try to delete the command themselves. This flag * declares that a delete is in progress and that recursive deletes should * be ignored. */ cmdPtr->flags |= CMD_IS_DELETED; /* |
︙ | ︙ | |||
7718 7719 7720 7721 7722 7723 7724 | return TCL_ERROR; } if (!(iPtr->flags & RAND_SEED_INITIALIZED)) { iPtr->flags |= RAND_SEED_INITIALIZED; /* | > | < | 7718 7719 7720 7721 7722 7723 7724 7725 7726 7727 7728 7729 7730 7731 7732 7733 | return TCL_ERROR; } if (!(iPtr->flags & RAND_SEED_INITIALIZED)) { iPtr->flags |= RAND_SEED_INITIALIZED; /* * To ensure different seeds in different threads (bug #416643), * take into consideration the thread this interp is running in. */ iPtr->randSeed = TclpGetClicks() + (PTR2INT(Tcl_GetCurrentThread())<<12); /* * Make sure 1 <= randSeed <= (2^31) - 2. See below. */ |
︙ | ︙ | |||
9087 9088 9089 9090 9091 9092 9093 | corPtr->callerEEPtr = iPtr->execEnvPtr; RESTORE_CONTEXT(corPtr->running); iPtr->execEnvPtr = corPtr->eePtr; TclNRAddCallback(interp, NRCoroutineExitCallback, corPtr, NULL, NULL, NULL); | | | 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 | corPtr->callerEEPtr = iPtr->execEnvPtr; RESTORE_CONTEXT(corPtr->running); iPtr->execEnvPtr = corPtr->eePtr; TclNRAddCallback(interp, NRCoroutineExitCallback, corPtr, NULL, NULL, NULL); /* ensure that the command is looked up in the correct namespace */ iPtr->lookupNsPtr = lookupNsPtr; Tcl_NREvalObj(interp, Tcl_NewListObj(objc-2, objv+2), 0); iPtr->numLevels--; SAVE_CONTEXT(corPtr->running); RESTORE_CONTEXT(corPtr->caller); iPtr->execEnvPtr = corPtr->callerEEPtr; |
︙ | ︙ |
Changes to generic/tclFileName.c.
︙ | ︙ | |||
1900 1901 1902 1903 1904 1905 1906 | &driveNameLen, &driveName) == TCL_PATH_ABSOLUTE) { pathPrefix = driveName; tail += driveNameLen; } } /* | | | 1900 1901 1902 1903 1904 1905 1906 1907 1908 1909 1910 1911 1912 1913 1914 | &driveNameLen, &driveName) == TCL_PATH_ABSOLUTE) { pathPrefix = driveName; tail += driveNameLen; } } /* * To process a [glob] invocation, this function may be called multiple * times. Each time, the previously discovered filenames are in the * interpreter result. We stash that away here so the result is free for * error messsages. */ savedResultObj = Tcl_GetObjResult(interp); Tcl_IncrRefCount(savedResultObj); |
︙ | ︙ |
Changes to generic/tclOO.c.
︙ | ︙ | |||
876 877 878 879 880 881 882 | } /* * The namespace is only deleted if it hasn't already been deleted. [Bug * 2950259] */ | | | 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 | } /* * The namespace is only deleted if it hasn't already been deleted. [Bug * 2950259] */ if (oPtr->namespacePtr && ((Namespace *) oPtr->namespacePtr)->earlyDeleteProc != NULL) { Tcl_DeleteNamespace(oPtr->namespacePtr); } if (oPtr->classPtr) { DelRef(oPtr->classPtr); } DelRef(fPtr->classCls->thisPtr); DelRef(fPtr->objectCls->thisPtr); |
︙ | ︙ | |||
1164 1165 1166 1167 1168 1169 1170 | * being deleted. */ { Object *oPtr = clientData; FOREACH_HASH_DECLS; Class *clsPtr = oPtr->classPtr, *mixinPtr; Method *mPtr; Tcl_Obj *filterObj, *variableObj; | | > > > > > > > > | 1164 1165 1166 1167 1168 1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 | * being deleted. */ { Object *oPtr = clientData; FOREACH_HASH_DECLS; Class *clsPtr = oPtr->classPtr, *mixinPtr; Method *mPtr; Tcl_Obj *filterObj, *variableObj; int deleteAlreadyInProgress = 0, i; /* * Instruct everyone to no longer use any allocated fields of the object. * Also delete the commands that refer to the object at this point (if * they still exist) because otherwise their references to the object * point into freed memory, allowing crashes. */ if (oPtr->command) { if ((((Command *)oPtr->command)->flags && CMD_IS_DELETED)) { /* * Namespace deletion must have been triggered by a trace on command * deletion , meaning that */ deleteAlreadyInProgress = 1; } Tcl_DeleteCommandFromToken(oPtr->fPtr->interp, oPtr->command); } if (oPtr->myCommand) { Tcl_DeleteCommandFromToken(oPtr->fPtr->interp, oPtr->myCommand); } /* |
︙ | ︙ | |||
1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288 1289 1290 | ClearMixins(clsPtr); ClearSuperclasses(clsPtr); if (clsPtr->subclasses.list) { ckfree(clsPtr->subclasses.list); clsPtr->subclasses.num = 0; } if (clsPtr->instances.list) { ckfree(clsPtr->instances.list); clsPtr->instances.num = 0; } if (clsPtr->mixinSubs.list) { ckfree(clsPtr->mixinSubs.list); clsPtr->mixinSubs.num = 0; } FOREACH_HASH_VALUE(mPtr, &clsPtr->classMethods) { TclOODelMethodRef(mPtr); } Tcl_DeleteHashTable(&clsPtr->classMethods); | > > > | 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 | ClearMixins(clsPtr); ClearSuperclasses(clsPtr); if (clsPtr->subclasses.list) { ckfree(clsPtr->subclasses.list); clsPtr->subclasses.list = NULL; clsPtr->subclasses.num = 0; } if (clsPtr->instances.list) { ckfree(clsPtr->instances.list); clsPtr->instances.list = NULL; clsPtr->instances.num = 0; } if (clsPtr->mixinSubs.list) { ckfree(clsPtr->mixinSubs.list); clsPtr->mixinSubs.list = NULL; clsPtr->mixinSubs.num = 0; } FOREACH_HASH_VALUE(mPtr, &clsPtr->classMethods) { TclOODelMethodRef(mPtr); } Tcl_DeleteHashTable(&clsPtr->classMethods); |
︙ | ︙ | |||
1301 1302 1303 1304 1305 1306 1307 | DelRef(clsPtr); } /* * Delete the object structure itself. */ | > > > > | > > | 1312 1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329 1330 1331 1332 | DelRef(clsPtr); } /* * Delete the object structure itself. */ if (deleteAlreadyInProgress) { oPtr->classPtr = NULL; oPtr->namespacePtr = NULL; } else { DelRef(oPtr); } } /* * ---------------------------------------------------------------------- * * TclOORemoveFromInstances -- * |
︙ | ︙ | |||
2429 2430 2431 2432 2433 2434 2435 | } /* * ---------------------------------------------------------------------- * * PublicObjectCmd, PrivateObjectCmd, TclOOInvokeObject -- * | | | 2446 2447 2448 2449 2450 2451 2452 2453 2454 2455 2456 2457 2458 2459 2460 | } /* * ---------------------------------------------------------------------- * * PublicObjectCmd, PrivateObjectCmd, TclOOInvokeObject -- * * Main entry point for object invocations. The Public* and Private* * wrapper functions (implementations of both object instance commands * and [my]) are just thin wrappers round the main TclOOObjectCmdCore * function. Note that the core is function is NRE-aware. * * ---------------------------------------------------------------------- */ |
︙ | ︙ | |||
2514 2515 2516 2517 2518 2519 2520 | } /* * ---------------------------------------------------------------------- * * TclOOObjectCmdCore, FinalizeObjectCall -- * | | | | | 2531 2532 2533 2534 2535 2536 2537 2538 2539 2540 2541 2542 2543 2544 2545 2546 2547 2548 2549 2550 2551 2552 2553 2554 2555 2556 2557 2558 | } /* * ---------------------------------------------------------------------- * * TclOOObjectCmdCore, FinalizeObjectCall -- * * Main function for object invocations. Does call chain creation, * management and invocation. The function FinalizeObjectCall exists to * clean up after the non-recursive processing of TclOOObjectCmdCore. * * ---------------------------------------------------------------------- */ int TclOOObjectCmdCore( Object *oPtr, /* The object being invoked. */ Tcl_Interp *interp, /* The interpreter containing the object. */ int objc, /* How many arguments are being passed in. */ Tcl_Obj *const *objv, /* The array of arguments. */ int flags, /* Whether this is an invocation through the * public or the private command interface. */ Class *startCls) /* Where to start in the call chain, or NULL * if we are to start at the front with * filters and the object's methods (which is * the normal case). */ { CallContext *contextPtr; |
︙ | ︙ | |||
2716 2717 2718 2719 2720 2721 2722 | } /* * Advance to the next method implementation in the chain in the method * call context while we process the body. However, need to adjust the * argument-skip control because we're guaranteed to have a single prefix * arg (i.e., 'next') and not the variable amount that can happen because | | | 2733 2734 2735 2736 2737 2738 2739 2740 2741 2742 2743 2744 2745 2746 2747 | } /* * Advance to the next method implementation in the chain in the method * call context while we process the body. However, need to adjust the * argument-skip control because we're guaranteed to have a single prefix * arg (i.e., 'next') and not the variable amount that can happen because * method invocations (i.e., '$obj meth' and 'my meth'), constructors * (i.e., '$cls new' and '$cls create obj') and destructors (no args at * all) come through the same code. */ contextPtr->index++; contextPtr->skip = skip; |
︙ | ︙ | |||
2785 2786 2787 2788 2789 2790 2791 | } /* * Advance to the next method implementation in the chain in the method * call context while we process the body. However, need to adjust the * argument-skip control because we're guaranteed to have a single prefix * arg (i.e., 'next') and not the variable amount that can happen because | | | 2802 2803 2804 2805 2806 2807 2808 2809 2810 2811 2812 2813 2814 2815 2816 | } /* * Advance to the next method implementation in the chain in the method * call context while we process the body. However, need to adjust the * argument-skip control because we're guaranteed to have a single prefix * arg (i.e., 'next') and not the variable amount that can happen because * method invocations (i.e., '$obj meth' and 'my meth'), constructors * (i.e., '$cls new' and '$cls create obj') and destructors (no args at * all) come through the same code. */ TclNRAddCallback(interp, FinalizeNext, contextPtr, INT2PTR(contextPtr->index), INT2PTR(contextPtr->skip), NULL); contextPtr->index++; |
︙ | ︙ |
Changes to generic/tclOOCall.c.
︙ | ︙ | |||
229 230 231 232 233 234 235 | /* * ---------------------------------------------------------------------- * * TclOOInvokeContext -- * * Invokes a single step along a method call-chain context. Note that the | | | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | /* * ---------------------------------------------------------------------- * * TclOOInvokeContext -- * * Invokes a single step along a method call-chain context. Note that the * invocation of a step along the chain can cause further steps along the * chain to be invoked. Note that this function is written to be as light * in stack usage as possible. * * ---------------------------------------------------------------------- */ int |
︙ | ︙ | |||
826 827 828 829 830 831 832 | for (i=cbPtr->filterLength ; i<callPtr->numChain ; i++) { if (callPtr->chain[i].mPtr == mPtr && callPtr->chain[i].isFilter == (doneFilters != NULL)) { /* * Call chain semantics states that methods come as *late* in the * call chain as possible. This is done by copying down the * following methods. Note that this does not change the number of | | | 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 | for (i=cbPtr->filterLength ; i<callPtr->numChain ; i++) { if (callPtr->chain[i].mPtr == mPtr && callPtr->chain[i].isFilter == (doneFilters != NULL)) { /* * Call chain semantics states that methods come as *late* in the * call chain as possible. This is done by copying down the * following methods. Note that this does not change the number of * method invocations in the call chain; it just rearranges them. */ Class *declCls = callPtr->chain[i].filterDeclarer; for (; i+1<callPtr->numChain ; i++) { callPtr->chain[i] = callPtr->chain[i+1]; } |
︙ | ︙ | |||
931 932 933 934 935 936 937 | /* * ---------------------------------------------------------------------- * * TclOOGetCallContext -- * * Responsible for constructing the call context, an ordered list of all | | | 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 | /* * ---------------------------------------------------------------------- * * TclOOGetCallContext -- * * Responsible for constructing the call context, an ordered list of all * method implementations to be called as part of a method invocation. * This method is central to the whole operation of the OO system. * * ---------------------------------------------------------------------- */ CallContext * TclOOGetCallContext( |
︙ | ︙ | |||
1513 1514 1515 1516 1517 1518 1519 | Tcl_IncrRefCount(methodLiteral); objectLiteral = Tcl_NewStringObj("object", -1); Tcl_IncrRefCount(objectLiteral); /* * Do the actual construction of the descriptions. They consist of a list * of triples that describe the details of how a method is understood. For | | | 1513 1514 1515 1516 1517 1518 1519 1520 1521 1522 1523 1524 1525 1526 1527 | Tcl_IncrRefCount(methodLiteral); objectLiteral = Tcl_NewStringObj("object", -1); Tcl_IncrRefCount(objectLiteral); /* * Do the actual construction of the descriptions. They consist of a list * of triples that describe the details of how a method is understood. For * each triple, the first word is the type of invocation ("method" is * normal, "unknown" is special because it adds the method name as an * extra argument when handled by some method types, and "filter" is * special because it's a filter method). The second word is the name of * the method in question (which differs for "unknown" and "filter" types) * and the third word is the full name of the class that declares the * method (or "object" if it is declared on the instance). */ |
︙ | ︙ |
Changes to generic/tclOOInt.h.
︙ | ︙ | |||
145 146 147 148 149 150 151 | * Now, the definition of what an object actually is. */ typedef struct Object { struct Foundation *fPtr; /* The basis for the object system. Putting * this here allows the avoidance of quite a * lot of hash lookups on the critical path | | | | | | | | 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 | * Now, the definition of what an object actually is. */ typedef struct Object { struct Foundation *fPtr; /* The basis for the object system. Putting * this here allows the avoidance of quite a * lot of hash lookups on the critical path * for object invocation and creation. */ Tcl_Namespace *namespacePtr;/* This object's namespace. */ Tcl_Command command; /* Reference to this object's public * command. */ Tcl_Command myCommand; /* Reference to this object's internal * command. */ struct Class *selfCls; /* This object's class. */ Tcl_HashTable *methodsPtr; /* Object-local Tcl_Obj (method name) to * Method* mapping. */ LIST_STATIC(struct Class *) mixins; /* Classes mixed into this object. */ LIST_STATIC(Tcl_Obj *) filters; /* List of filter names. */ struct Class *classPtr; /* This is non-NULL for all classes, and NULL * for everything else. It points to the class * structure. */ int refCount; /* Number of strong references to this object. * Note that there may be many more weak * references; this mechanism exists to * avoid Tcl_Preserve. */ int flags; int creationEpoch; /* Unique value to make comparisons of objects * easier. */ int epoch; /* Per-object epoch, incremented when the way * an object should resolve call chains is * changed. */ |
︙ | ︙ | |||
319 320 321 322 323 324 325 | * destructor. */ Tcl_Obj *clonedName; /* Shared object containing the name of a * "<cloned>" pseudo-constructor. */ Tcl_Obj *defineName; /* Fully qualified name of oo::define. */ } Foundation; /* | | | | 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 | * destructor. */ Tcl_Obj *clonedName; /* Shared object containing the name of a * "<cloned>" pseudo-constructor. */ Tcl_Obj *defineName; /* Fully qualified name of oo::define. */ } Foundation; /* * A call context structure is built when a method is called. It contains the * chain of method implementations that are to be invoked by a particular * call, and the process of calling walks the chain, with the [next] command * proceeding to the next entry in the chain. */ #define CALL_CHAIN_STATIC_SIZE 4 struct MInvoke { Method *mPtr; /* Reference to the method implementation * record. */ int isFilter; /* Whether this is a filter invocation. */ Class *filterDeclarer; /* What class decided to add the filter; if * NULL, it was added by the object. */ }; typedef struct CallChain { int objectCreationEpoch; /* The object's creation epoch. Note that the * object reference is not stored in the call |
︙ | ︙ |
Changes to tests/oo.test.
︙ | ︙ | |||
1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 | oo::class create bar oo::define bar superclass bar1 bar2 bar create foo set result [list [catch {bar create foo} msg] $msg] lappend result [bar0 destroy] [oo::object create foo] [foo destroy] \ [oo::object create bar2] [bar2 destroy] } {1 {can't create object "foo": command already exists with that name} destroyed {} ::foo {} ::bar2 {}} test oo-12.1 {OO: filters} { oo::class create Aclass Aclass create Aobject oo::define Aclass { method concatenate args { global result | > > > > > > > > > > > > | 1478 1479 1480 1481 1482 1483 1484 1485 1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 | oo::class create bar oo::define bar superclass bar1 bar2 bar create foo set result [list [catch {bar create foo} msg] $msg] lappend result [bar0 destroy] [oo::object create foo] [foo destroy] \ [oo::object create bar2] [bar2 destroy] } {1 {can't create object "foo": command already exists with that name} destroyed {} ::foo {} ::bar2 {}} test oo-11.5 {OO: cleanup} { oo::class create obj1 trace add command obj1 delete {apply {{name1 name2 action} { set namespace [info object namespace $name1] namespace delete $namespace }}} rename obj1 {} # No segmentation fault return done } done test oo-12.1 {OO: filters} { oo::class create Aclass Aclass create Aobject oo::define Aclass { method concatenate args { global result |
︙ | ︙ |