diff options
Diffstat (limited to 'generic/tclOOCall.c')
| -rw-r--r-- | generic/tclOOCall.c | 2368 |
1 files changed, 0 insertions, 2368 deletions
diff --git a/generic/tclOOCall.c b/generic/tclOOCall.c deleted file mode 100644 index 85ca995..0000000 --- a/generic/tclOOCall.c +++ /dev/null @@ -1,2368 +0,0 @@ -/* - * tclOOCall.c -- - * - * This file contains the method call chain management code for the - * object-system core. It also contains everything else that does - * inheritance hierarchy traversal. - * - * Copyright © 2005-2019 Donal K. Fellows - * - * See the file "license.terms" for information on usage and redistribution of - * this file, and for a DISCLAIMER OF ALL WARRANTIES. - */ - -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif -#include "tclInt.h" -#include "tclOOInt.h" -#include <assert.h> - -/* - * Structure containing a CallContext and any other values needed only during - * the construction of the CallContext. - */ - -struct ChainBuilder { - CallChain *callChainPtr; /* The call chain being built. */ - int filterLength; /* Number of entries in the call chain that - * are due to processing filters and not the - * main call chain. */ - Object *oPtr; /* The object that we are building the chain - * for. */ -}; - -/* - * Structures used for traversing the class hierarchy to find out where - * definitions are supposed to be done. - */ - -typedef struct { - Class *definerCls; - Tcl_Obj *namespaceName; -} DefineEntry; - -typedef struct { - DefineEntry *list; - int num; - int size; -} DefineChain; - -/* - * Extra flags used for call chain management. - */ - -#define DEFINITE_PROTECTED 0x100000 -#define DEFINITE_PUBLIC 0x200000 -#define KNOWN_STATE (DEFINITE_PROTECTED | DEFINITE_PUBLIC) -#define SPECIAL (CONSTRUCTOR | DESTRUCTOR | FORCE_UNKNOWN) -#define BUILDING_MIXINS 0x400000 -#define TRAVERSED_MIXIN 0x800000 -#define OBJECT_MIXIN 0x1000000 -#define DEFINE_FOR_CLASS 0x2000000 -#define MIXIN_CONSISTENT(flags) \ - (((flags) & OBJECT_MIXIN) || \ - !((flags) & BUILDING_MIXINS) == !((flags) & TRAVERSED_MIXIN)) - -/* - * Note that the flag bit PRIVATE_METHOD has a confusing name; it's just for - * Itcl's special type of private. - */ - -#define IS_PUBLIC(mPtr) \ - (((mPtr)->flags & PUBLIC_METHOD) != 0) -#define IS_UNEXPORTED(mPtr) \ - (((mPtr)->flags & SCOPE_FLAGS) == 0) -#define IS_ITCLPRIVATE(mPtr) \ - (((mPtr)->flags & PRIVATE_METHOD) != 0) -#define IS_PRIVATE(mPtr) \ - (((mPtr)->flags & TRUE_PRIVATE_METHOD) != 0) -#define WANT_PUBLIC(flags) \ - (((flags) & PUBLIC_METHOD) != 0) -#define WANT_UNEXPORTED(flags) \ - (((flags) & (PRIVATE_METHOD | TRUE_PRIVATE_METHOD)) == 0) -#define WANT_ITCLPRIVATE(flags) \ - (((flags) & PRIVATE_METHOD) != 0) -#define WANT_PRIVATE(flags) \ - (((flags) & TRUE_PRIVATE_METHOD) != 0) - -/* - * Function declarations for things defined in this file. - */ - -static void AddClassFiltersToCallContext(Object *const oPtr, - Class *clsPtr, struct ChainBuilder *const cbPtr, - Tcl_HashTable *const doneFilters, int flags); -static void AddClassMethodNames(Class *clsPtr, int flags, - Tcl_HashTable *const namesPtr, - Tcl_HashTable *const examinedClassesPtr); -static inline void AddDefinitionNamespaceToChain(Class *const definerCls, - Tcl_Obj *const namespaceName, - DefineChain *const definePtr, int flags); -static inline void AddMethodToCallChain(Method *const mPtr, - struct ChainBuilder *const cbPtr, - Tcl_HashTable *const doneFilters, - Class *const filterDecl, int flags); -static inline int AddInstancePrivateToCallContext(Object *const oPtr, - Tcl_Obj *const methodNameObj, - struct ChainBuilder *const cbPtr, int flags); -static inline void AddStandardMethodName(int flags, Tcl_Obj *namePtr, - Method *mPtr, Tcl_HashTable *namesPtr); -static inline void AddPrivateMethodNames(Tcl_HashTable *methodsTablePtr, - Tcl_HashTable *namesPtr); -static inline int AddSimpleChainToCallContext(Object *const oPtr, - Class *const contextCls, - Tcl_Obj *const methodNameObj, - struct ChainBuilder *const cbPtr, - Tcl_HashTable *const doneFilters, int flags, - Class *const filterDecl); -static int AddPrivatesFromClassChainToCallContext(Class *classPtr, - Class *const contextCls, - Tcl_Obj *const methodNameObj, - struct ChainBuilder *const cbPtr, - Tcl_HashTable *const doneFilters, int flags, - Class *const filterDecl); -static int AddSimpleClassChainToCallContext(Class *classPtr, - Tcl_Obj *const methodNameObj, - struct ChainBuilder *const cbPtr, - Tcl_HashTable *const doneFilters, int flags, - Class *const filterDecl); -static void AddSimpleClassDefineNamespaces(Class *classPtr, - DefineChain *const definePtr, int flags); -static inline void AddSimpleDefineNamespaces(Object *const oPtr, - DefineChain *const definePtr, int flags); -static int CmpStr(const void *ptr1, const void *ptr2); -static void DupMethodNameRep(Tcl_Obj *srcPtr, Tcl_Obj *dstPtr); -static Tcl_NRPostProc FinalizeMethodRefs; -static void FreeMethodNameRep(Tcl_Obj *objPtr); -static inline int IsStillValid(CallChain *callPtr, Object *oPtr, - int flags, int reuseMask); -static Tcl_NRPostProc ResetFilterFlags; -static Tcl_NRPostProc SetFilterFlags; -static int SortMethodNames(Tcl_HashTable *namesPtr, int flags, - const char ***stringsPtr); -static inline void StashCallChain(Tcl_Obj *objPtr, CallChain *callPtr); - -/* - * Object type used to manage type caches attached to method names. - */ - -static const Tcl_ObjType methodNameType = { - "TclOO method name", - FreeMethodNameRep, - DupMethodNameRep, - NULL, - NULL -}; - - -/* - * ---------------------------------------------------------------------- - * - * TclOODeleteContext -- - * - * Destroys a method call-chain context, which should not be in use. - * - * ---------------------------------------------------------------------- - */ - -void -TclOODeleteContext( - CallContext *contextPtr) -{ - Object *oPtr = contextPtr->oPtr; - - TclOODeleteChain(contextPtr->callPtr); - if (oPtr != NULL) { - TclStackFree(oPtr->fPtr->interp, contextPtr); - - /* - * Corresponding AddRef() in TclOO.c/TclOOObjectCmdCore - */ - - TclOODecrRefCount(oPtr); - } -} - -/* - * ---------------------------------------------------------------------- - * - * TclOODeleteChainCache -- - * - * Destroy the cache of method call-chains. - * - * ---------------------------------------------------------------------- - */ - -void -TclOODeleteChainCache( - Tcl_HashTable *tablePtr) -{ - FOREACH_HASH_DECLS; - CallChain *callPtr; - - FOREACH_HASH_VALUE(callPtr, tablePtr) { - if (callPtr) { - TclOODeleteChain(callPtr); - } - } - Tcl_DeleteHashTable(tablePtr); - ckfree(tablePtr); -} - -/* - * ---------------------------------------------------------------------- - * - * TclOODeleteChain -- - * - * Destroys a method call-chain. - * - * ---------------------------------------------------------------------- - */ - -void -TclOODeleteChain( - CallChain *callPtr) -{ - if (callPtr == NULL || callPtr->refCount-- > 1) { - return; - } - if (callPtr->chain != callPtr->staticChain) { - ckfree(callPtr->chain); - } - ckfree(callPtr); -} - -/* - * ---------------------------------------------------------------------- - * - * TclOOStashContext -- - * - * Saves a reference to a method call context in a Tcl_Obj's internal - * representation. - * - * ---------------------------------------------------------------------- - */ - -static inline void -StashCallChain( - Tcl_Obj *objPtr, - CallChain *callPtr) -{ - Tcl_ObjInternalRep ir; - - callPtr->refCount++; - TclGetString(objPtr); - ir.twoPtrValue.ptr1 = callPtr; - Tcl_StoreInternalRep(objPtr, &methodNameType, &ir); -} - -void -TclOOStashContext( - Tcl_Obj *objPtr, - CallContext *contextPtr) -{ - StashCallChain(objPtr, contextPtr->callPtr); -} - -/* - * ---------------------------------------------------------------------- - * - * DupMethodNameRep, FreeMethodNameRep -- - * - * Functions to implement the required parts of the Tcl_Obj guts needed - * for caching of method contexts in Tcl_Objs. - * - * ---------------------------------------------------------------------- - */ - -static void -DupMethodNameRep( - Tcl_Obj *srcPtr, - Tcl_Obj *dstPtr) -{ - StashCallChain(dstPtr, - (CallChain *)TclFetchInternalRep(srcPtr, &methodNameType)->twoPtrValue.ptr1); -} - -static void -FreeMethodNameRep( - Tcl_Obj *objPtr) -{ - TclOODeleteChain( - (CallChain *)TclFetchInternalRep(objPtr, &methodNameType)->twoPtrValue.ptr1); -} - -/* - * ---------------------------------------------------------------------- - * - * 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 -TclOOInvokeContext( - void *clientData, /* The method call context. */ - Tcl_Interp *interp, /* Interpreter for error reporting, and many - * other sorts of context handling (e.g., - * commands, variables) depending on method - * implementation. */ - int objc, /* The number of arguments. */ - Tcl_Obj *const objv[]) /* The arguments as actually seen. */ -{ - CallContext *const contextPtr = (CallContext *)clientData; - Method *const mPtr = contextPtr->callPtr->chain[contextPtr->index].mPtr; - const int isFilter = - contextPtr->callPtr->chain[contextPtr->index].isFilter; - - /* - * If this is the first step along the chain, we preserve the method - * entries in the chain so that they do not get deleted out from under our - * feet. - */ - - if (contextPtr->index == 0) { - int i; - - for (i = 0 ; i < contextPtr->callPtr->numChain ; i++) { - AddRef(contextPtr->callPtr->chain[i].mPtr); - } - - /* - * Ensure that the method name itself is part of the arguments when - * we're doing unknown processing. - */ - - if (contextPtr->callPtr->flags & OO_UNKNOWN_METHOD) { - contextPtr->skip--; - } - - /* - * Add a callback to ensure that method references are dropped once - * this call is finished. - */ - - TclNRAddCallback(interp, FinalizeMethodRefs, contextPtr, NULL, NULL, - NULL); - } - - /* - * Save whether we were in a filter and set up whether we are now. - */ - - if (contextPtr->oPtr->flags & FILTER_HANDLING) { - TclNRAddCallback(interp, SetFilterFlags, contextPtr, NULL,NULL,NULL); - } else { - TclNRAddCallback(interp, ResetFilterFlags,contextPtr,NULL,NULL,NULL); - } - if (isFilter || contextPtr->callPtr->flags & FILTER_HANDLING) { - contextPtr->oPtr->flags |= FILTER_HANDLING; - } else { - contextPtr->oPtr->flags &= ~FILTER_HANDLING; - } - - /* - * Run the method implementation. - */ - - return (mPtr->typePtr->callProc)(mPtr->clientData, interp, - (Tcl_ObjectContext) contextPtr, objc, objv); -} - -static int -SetFilterFlags( - void *data[], - TCL_UNUSED(Tcl_Interp *), - int result) -{ - CallContext *contextPtr = (CallContext *)data[0]; - - contextPtr->oPtr->flags |= FILTER_HANDLING; - return result; -} - -static int -ResetFilterFlags( - void *data[], - TCL_UNUSED(Tcl_Interp *), - int result) -{ - CallContext *contextPtr = (CallContext *)data[0]; - - contextPtr->oPtr->flags &= ~FILTER_HANDLING; - return result; -} - -static int -FinalizeMethodRefs( - void *data[], - TCL_UNUSED(Tcl_Interp *), - int result) -{ - CallContext *contextPtr = (CallContext *)data[0]; - int i; - - for (i = 0 ; i < contextPtr->callPtr->numChain ; i++) { - TclOODelMethodRef(contextPtr->callPtr->chain[i].mPtr); - } - return result; -} - -/* - * ---------------------------------------------------------------------- - * - * TclOOGetSortedMethodList, TclOOGetSortedClassMethodList -- - * - * Discovers the list of method names supported by an object or class. - * - * ---------------------------------------------------------------------- - */ - -int -TclOOGetSortedMethodList( - Object *oPtr, /* The object to get the method names for. */ - Object *contextObj, /* From what context object we are inquiring. - * NULL when the context shouldn't see - * object-level private methods. Note that - * flags can override this. */ - Class *contextCls, /* From what context class we are inquiring. - * NULL when the context shouldn't see - * class-level private methods. Note that - * flags can override this. */ - int flags, /* Whether we just want the public method - * names. */ - const char ***stringsPtr) /* Where to write a pointer to the array of - * strings to. */ -{ - Tcl_HashTable names; /* Tcl_Obj* method name to "wanted in list" - * mapping. */ - Tcl_HashTable examinedClasses; - /* Used to track what classes have been looked - * at. Is set-like in nature and keyed by - * pointer to class. */ - FOREACH_HASH_DECLS; - int i, numStrings; - Class *mixinPtr; - Tcl_Obj *namePtr; - Method *mPtr; - - Tcl_InitObjHashTable(&names); - Tcl_InitHashTable(&examinedClasses, TCL_ONE_WORD_KEYS); - - /* - * Name the bits used in the names table values. - */ -#define IN_LIST 1 -#define NO_IMPLEMENTATION 2 - - /* - * Process method names due to the object. - */ - - if (oPtr->methodsPtr) { - FOREACH_HASH(namePtr, mPtr, oPtr->methodsPtr) { - if (IS_PRIVATE(mPtr)) { - continue; - } - if (IS_UNEXPORTED(mPtr) && !WANT_UNEXPORTED(flags)) { - continue; - } - AddStandardMethodName(flags, namePtr, mPtr, &names); - } - } - - /* - * Process method names due to private methods on the object's class. - */ - - if (WANT_UNEXPORTED(flags)) { - FOREACH_HASH(namePtr, mPtr, &oPtr->selfCls->classMethods) { - if (IS_UNEXPORTED(mPtr)) { - AddStandardMethodName(flags, namePtr, mPtr, &names); - } - } - } - - /* - * Process method names due to private methods on the context's object or - * class. Which must be correct if either are not NULL. - */ - - if (contextObj && contextObj->methodsPtr) { - AddPrivateMethodNames(contextObj->methodsPtr, &names); - } - if (contextCls) { - AddPrivateMethodNames(&contextCls->classMethods, &names); - } - - /* - * Process (normal) method names from the class hierarchy and the mixin - * hierarchy. - */ - - AddClassMethodNames(oPtr->selfCls, flags, &names, &examinedClasses); - FOREACH(mixinPtr, oPtr->mixins) { - AddClassMethodNames(mixinPtr, flags | TRAVERSED_MIXIN, &names, - &examinedClasses); - } - - /* - * Tidy up, sort the names and resolve finally whether we really want - * them (processing export layering). - */ - - Tcl_DeleteHashTable(&examinedClasses); - numStrings = SortMethodNames(&names, flags, stringsPtr); - Tcl_DeleteHashTable(&names); - return numStrings; -} - -int -TclOOGetSortedClassMethodList( - Class *clsPtr, /* The class to get the method names for. */ - int flags, /* Whether we just want the public method - * names. */ - const char ***stringsPtr) /* Where to write a pointer to the array of - * strings to. */ -{ - Tcl_HashTable names; /* Tcl_Obj* method name to "wanted in list" - * mapping. */ - Tcl_HashTable examinedClasses; - /* Used to track what classes have been looked - * at. Is set-like in nature and keyed by - * pointer to class. */ - int numStrings; - - Tcl_InitObjHashTable(&names); - Tcl_InitHashTable(&examinedClasses, TCL_ONE_WORD_KEYS); - - /* - * Process method names from the class hierarchy and the mixin hierarchy. - */ - - AddClassMethodNames(clsPtr, flags, &names, &examinedClasses); - Tcl_DeleteHashTable(&examinedClasses); - - /* - * Process private method names if we should. [TIP 500] - */ - - if (WANT_PRIVATE(flags)) { - AddPrivateMethodNames(&clsPtr->classMethods, &names); - flags &= ~TRUE_PRIVATE_METHOD; - } - - /* - * Tidy up, sort the names and resolve finally whether we really want - * them (processing export layering). - */ - - numStrings = SortMethodNames(&names, flags, stringsPtr); - Tcl_DeleteHashTable(&names); - return numStrings; -} - -/* - * ---------------------------------------------------------------------- - * - * SortMethodNames -- - * - * Shared helper for TclOOGetSortedMethodList etc. that knows the method - * sorting rules. - * - * Returns: - * The length of the sorted list. - * - * ---------------------------------------------------------------------- - */ - -static int -SortMethodNames( - Tcl_HashTable *namesPtr, /* The table of names; unsorted, but contains - * whether the names are wanted and under what - * circumstances. */ - int flags, /* Whether we are looking for unexported - * methods. Full private methods are handled - * on insertion to the table. */ - const char ***stringsPtr) /* Where to store the sorted list of strings - * that we produce. ckalloced() */ -{ - const char **strings; - FOREACH_HASH_DECLS; - Tcl_Obj *namePtr; - void *isWanted; - int i = 0; - - /* - * See how many (visible) method names there are. If none, we do not (and - * should not) try to sort the list of them. - */ - - if (namesPtr->numEntries == 0) { - *stringsPtr = NULL; - return 0; - } - - /* - * We need to build the list of methods to sort. We will be using qsort() - * for this, because it is very unlikely that the list will be heavily - * sorted when it is long enough to matter. - */ - - strings = (const char **)ckalloc(sizeof(char *) * namesPtr->numEntries); - FOREACH_HASH(namePtr, isWanted, namesPtr) { - if (!WANT_PUBLIC(flags) || (PTR2INT(isWanted) & IN_LIST)) { - if (PTR2INT(isWanted) & NO_IMPLEMENTATION) { - continue; - } - strings[i++] = TclGetString(namePtr); - } - } - - /* - * Note that 'i' may well be less than names.numEntries when we are - * dealing with public method names. We don't sort unless there's at least - * two method names. - */ - - if (i > 0) { - if (i > 1) { - qsort((void *) strings, i, sizeof(char *), CmpStr); - } - *stringsPtr = strings; - } else { - ckfree(strings); - *stringsPtr = NULL; - } - return i; -} - -/* - * Comparator for SortMethodNames - */ - -static int -CmpStr( - const void *ptr1, - const void *ptr2) -{ - const char **strPtr1 = (const char **) ptr1; - const char **strPtr2 = (const char **) ptr2; - - return TclpUtfNcmp2(*strPtr1, *strPtr2, strlen(*strPtr1) + 1); -} - -/* - * ---------------------------------------------------------------------- - * - * AddClassMethodNames -- - * - * Adds the method names defined by a class (or its superclasses) to the - * collection being built. The collection is built in a hash table to - * ensure that duplicates are excluded. Helper for GetSortedMethodList(). - * - * ---------------------------------------------------------------------- - */ - -static void -AddClassMethodNames( - Class *clsPtr, /* Class to get method names from. */ - int flags, /* Whether we are interested in just the - * public method names. */ - Tcl_HashTable *const namesPtr, - /* Reference to the hash table to put the - * information in. The hash table maps the - * Tcl_Obj * method name to an integral value - * describing whether the method is wanted. - * This ensures that public/private override - * semantics are handled correctly. */ - Tcl_HashTable *const examinedClassesPtr) - /* Hash table that tracks what classes have - * already been looked at. The keys are the - * pointers to the classes, and the values are - * immaterial. */ -{ - int i; - - /* - * If we've already started looking at this class, stop working on it now - * to prevent repeated work. - */ - - if (Tcl_FindHashEntry(examinedClassesPtr, (char *) clsPtr)) { - return; - } - - /* - * Scope all declarations so that the compiler can stand a good chance of - * making the recursive step highly efficient. We also hand-implement the - * tail-recursive case using a while loop; C compilers typically cannot do - * tail-recursion optimization usefully. - */ - - while (1) { - FOREACH_HASH_DECLS; - Tcl_Obj *namePtr; - Method *mPtr; - int isNew; - - (void) Tcl_CreateHashEntry(examinedClassesPtr, (char *) clsPtr, - &isNew); - if (!isNew) { - break; - } - - if (clsPtr->mixins.num != 0) { - Class *mixinPtr; - - FOREACH(mixinPtr, clsPtr->mixins) { - if (mixinPtr != clsPtr) { - AddClassMethodNames(mixinPtr, flags|TRAVERSED_MIXIN, - namesPtr, examinedClassesPtr); - } - } - } - - FOREACH_HASH(namePtr, mPtr, &clsPtr->classMethods) { - AddStandardMethodName(flags, namePtr, mPtr, namesPtr); - } - - if (clsPtr->superclasses.num != 1) { - break; - } - clsPtr = clsPtr->superclasses.list[0]; - } - if (clsPtr->superclasses.num != 0) { - Class *superPtr; - - FOREACH(superPtr, clsPtr->superclasses) { - AddClassMethodNames(superPtr, flags, namesPtr, - examinedClassesPtr); - } - } -} - -/* - * ---------------------------------------------------------------------- - * - * AddPrivateMethodNames, AddStandardMethodName -- - * - * Factored-out helpers for the sorted name list production functions. - * - * ---------------------------------------------------------------------- - */ - -static inline void -AddPrivateMethodNames( - Tcl_HashTable *methodsTablePtr, - Tcl_HashTable *namesPtr) -{ - FOREACH_HASH_DECLS; - Method *mPtr; - Tcl_Obj *namePtr; - - FOREACH_HASH(namePtr, mPtr, methodsTablePtr) { - if (IS_PRIVATE(mPtr)) { - int isNew; - - hPtr = Tcl_CreateHashEntry(namesPtr, (char *) namePtr, &isNew); - Tcl_SetHashValue(hPtr, INT2PTR(IN_LIST)); - } - } -} - -static inline void -AddStandardMethodName( - int flags, - Tcl_Obj *namePtr, - Method *mPtr, - Tcl_HashTable *namesPtr) -{ - if (!IS_PRIVATE(mPtr)) { - int isNew; - Tcl_HashEntry *hPtr = - Tcl_CreateHashEntry(namesPtr, (char *) namePtr, &isNew); - - if (isNew) { - int isWanted = (!WANT_PUBLIC(flags) || IS_PUBLIC(mPtr)) - ? IN_LIST : 0; - - isWanted |= (mPtr->typePtr == NULL ? NO_IMPLEMENTATION : 0); - Tcl_SetHashValue(hPtr, INT2PTR(isWanted)); - } else if ((PTR2INT(Tcl_GetHashValue(hPtr)) & NO_IMPLEMENTATION) - && mPtr->typePtr != NULL) { - int isWanted = PTR2INT(Tcl_GetHashValue(hPtr)); - - isWanted &= ~NO_IMPLEMENTATION; - Tcl_SetHashValue(hPtr, INT2PTR(isWanted)); - } - } -} - -#undef IN_LIST -#undef NO_IMPLEMENTATION - -/* - * ---------------------------------------------------------------------- - * - * AddInstancePrivateToCallContext -- - * - * Add private methods from the instance. Called when the calling Tcl - * context is a TclOO method declared by an object that is the same as - * the current object. Returns true iff a private method was actually - * found and added to the call chain (as this suppresses caching). - * - * ---------------------------------------------------------------------- - */ - -static inline int -AddInstancePrivateToCallContext( - Object *const oPtr, /* Object to add call chain entries for. */ - Tcl_Obj *const methodName, /* Name of method to add the call chain - * entries for. */ - struct ChainBuilder *const cbPtr, - /* Where to add the call chain entries. */ - int flags) /* What sort of call chain are we building. */ -{ - Tcl_HashEntry *hPtr; - Method *mPtr; - int donePrivate = 0; - - if (oPtr->methodsPtr) { - hPtr = Tcl_FindHashEntry(oPtr->methodsPtr, (char *) methodName); - if (hPtr != NULL) { - mPtr = (Method *)Tcl_GetHashValue(hPtr); - if (IS_PRIVATE(mPtr)) { - AddMethodToCallChain(mPtr, cbPtr, NULL, NULL, flags); - donePrivate = 1; - } - } - } - return donePrivate; -} - -/* - * ---------------------------------------------------------------------- - * - * AddSimpleChainToCallContext -- - * - * The core of the call-chain construction engine, this handles calling a - * particular method on a particular object. Note that filters and - * unknown handling are already handled by the logic that uses this - * function. Returns true if a private method was one of those found. - * - * ---------------------------------------------------------------------- - */ - -static inline int -AddSimpleChainToCallContext( - Object *const oPtr, /* Object to add call chain entries for. */ - Class *const contextCls, /* Context class; the currently considered - * class is equal to this, private methods may - * also be added. [TIP 500] */ - Tcl_Obj *const methodNameObj, - /* Name of method to add the call chain - * entries for. */ - struct ChainBuilder *const cbPtr, - /* Where to add the call chain entries. */ - Tcl_HashTable *const doneFilters, - /* Where to record what call chain entries - * have been processed. */ - int flags, /* What sort of call chain are we building. */ - Class *const filterDecl) /* The class that declared the filter. If - * NULL, either the filter was declared by the - * object or this isn't a filter. */ -{ - int i, foundPrivate = 0, blockedUnexported = 0; - Tcl_HashEntry *hPtr; - Method *mPtr; - - if (!(flags & (KNOWN_STATE | SPECIAL)) && oPtr->methodsPtr) { - hPtr = Tcl_FindHashEntry(oPtr->methodsPtr, (char *) methodNameObj); - - if (hPtr != NULL) { - mPtr = (Method *)Tcl_GetHashValue(hPtr); - if (!IS_PRIVATE(mPtr)) { - if (WANT_PUBLIC(flags)) { - if (!IS_PUBLIC(mPtr)) { - blockedUnexported = 1; - } else { - flags |= DEFINITE_PUBLIC; - } - } else { - flags |= DEFINITE_PROTECTED; - } - } - } - } - if (!(flags & SPECIAL)) { - Class *mixinPtr; - - FOREACH(mixinPtr, oPtr->mixins) { - if (contextCls) { - foundPrivate |= AddPrivatesFromClassChainToCallContext( - mixinPtr, contextCls, methodNameObj, cbPtr, - doneFilters, flags|TRAVERSED_MIXIN, filterDecl); - } - foundPrivate |= AddSimpleClassChainToCallContext(mixinPtr, - methodNameObj, cbPtr, doneFilters, - flags | TRAVERSED_MIXIN, filterDecl); - } - if (oPtr->methodsPtr && !blockedUnexported) { - hPtr = Tcl_FindHashEntry(oPtr->methodsPtr, (char*) methodNameObj); - if (hPtr != NULL) { - mPtr = (Method *)Tcl_GetHashValue(hPtr); - if (!IS_PRIVATE(mPtr)) { - AddMethodToCallChain(mPtr, cbPtr, doneFilters, filterDecl, - flags); - } - } - } - } - if (contextCls) { - foundPrivate |= AddPrivatesFromClassChainToCallContext(oPtr->selfCls, - contextCls, methodNameObj, cbPtr, doneFilters, flags, - filterDecl); - } - if (!blockedUnexported) { - foundPrivate |= AddSimpleClassChainToCallContext(oPtr->selfCls, - methodNameObj, cbPtr, doneFilters, flags, filterDecl); - } - return foundPrivate; -} - -/* - * ---------------------------------------------------------------------- - * - * AddMethodToCallChain -- - * - * Utility method that manages the adding of a particular method - * implementation to a call-chain. - * - * ---------------------------------------------------------------------- - */ - -static inline void -AddMethodToCallChain( - Method *const mPtr, /* Actual method implementation to add to call - * chain (or NULL, a no-op). */ - struct ChainBuilder *const cbPtr, - /* The call chain to add the method - * implementation to. */ - Tcl_HashTable *const doneFilters, - /* Where to record what filters have been - * processed. If NULL, not processing filters. - * Note that this function does not update - * this hashtable. */ - Class *const filterDecl, /* The class that declared the filter. If - * NULL, either the filter was declared by the - * object or this isn't a filter. */ - int flags) /* Used to check if we're mixin-consistent - * only. Mixin-consistent means that either - * we're looking to add things from a mixin - * and we have passed a mixin, or we're not - * looking to add things from a mixin and have - * not passed a mixin. */ -{ - CallChain *callPtr = cbPtr->callChainPtr; - int i; - - /* - * Return if this is just an entry used to record whether this is a public - * method. If so, there's nothing real to call and so nothing to add to - * the call chain. - * - * This is also where we enforce mixin-consistency. - */ - - if (mPtr == NULL || mPtr->typePtr == NULL || !MIXIN_CONSISTENT(flags)) { - return; - } - - /* - * Enforce real private method handling here. We will skip adding this - * method IF - * 1) we are not allowing private methods, AND - * 2) this is a private method, AND - * 3) this is a class method, AND - * 4) this method was not declared by the class of the current object. - * - * This does mean that only classes really handle private methods. This - * should be sufficient for [incr Tcl] support though. - */ - - if (!WANT_UNEXPORTED(callPtr->flags) - && IS_UNEXPORTED(mPtr) - && (mPtr->declaringClassPtr != NULL) - && (mPtr->declaringClassPtr != cbPtr->oPtr->selfCls)) { - return; - } - - /* - * First test whether the method is already in the call chain. Skip over - * any leading filters. - */ - - 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]; - } - callPtr->chain[i].mPtr = mPtr; - callPtr->chain[i].isFilter = (doneFilters != NULL); - callPtr->chain[i].filterDeclarer = declCls; - return; - } - } - - /* - * Need to really add the method. This is made a bit more complex by the - * fact that we are using some "static" space initially, and only start - * realloc-ing if the chain gets long. - */ - - if (callPtr->numChain == CALL_CHAIN_STATIC_SIZE) { - callPtr->chain = - (struct MInvoke *)ckalloc(sizeof(struct MInvoke) * (callPtr->numChain + 1)); - memcpy(callPtr->chain, callPtr->staticChain, - sizeof(struct MInvoke) * callPtr->numChain); - } else if (callPtr->numChain > CALL_CHAIN_STATIC_SIZE) { - callPtr->chain = (struct MInvoke *)ckrealloc(callPtr->chain, - sizeof(struct MInvoke) * (callPtr->numChain + 1)); - } - callPtr->chain[i].mPtr = mPtr; - callPtr->chain[i].isFilter = (doneFilters != NULL); - callPtr->chain[i].filterDeclarer = filterDecl; - callPtr->numChain++; -} - -/* - * ---------------------------------------------------------------------- - * - * InitCallChain -- - * Encoding of the policy of how to set up a call chain. Doesn't populate - * the chain with the method implementation data. - * - * ---------------------------------------------------------------------- - */ - -static inline void -InitCallChain( - CallChain *callPtr, - Object *oPtr, - int flags) -{ - callPtr->flags = flags & - (PUBLIC_METHOD | PRIVATE_METHOD | SPECIAL | FILTER_HANDLING); - if (oPtr->flags & USE_CLASS_CACHE) { - oPtr = oPtr->selfCls->thisPtr; - callPtr->flags |= USE_CLASS_CACHE; - } - callPtr->epoch = oPtr->fPtr->epoch; - callPtr->objectCreationEpoch = oPtr->creationEpoch; - callPtr->objectEpoch = oPtr->epoch; - callPtr->refCount = 1; - callPtr->numChain = 0; - callPtr->chain = callPtr->staticChain; -} - -/* - * ---------------------------------------------------------------------- - * - * IsStillValid -- - * - * Calculates whether the given call chain can be used for executing a - * method for the given object. The condition on a chain from a cached - * location being reusable is: - * - Refers to the same object (same creation epoch), and - * - Still across the same class structure (same global epoch), and - * - Still across the same object structure (same local epoch), and - * - No public/private/filter magic leakage (same flags, modulo the fact - * that a public chain will satisfy a non-public call). - * - * ---------------------------------------------------------------------- - */ - -static inline int -IsStillValid( - CallChain *callPtr, - Object *oPtr, - int flags, - int mask) -{ - if ((oPtr->flags & USE_CLASS_CACHE)) { - oPtr = oPtr->selfCls->thisPtr; - flags |= USE_CLASS_CACHE; - } - return ((callPtr->objectCreationEpoch == oPtr->creationEpoch) - && (callPtr->epoch == oPtr->fPtr->epoch) - && (callPtr->objectEpoch == oPtr->epoch) - && ((callPtr->flags & mask) == (flags & mask))); -} - -/* - * ---------------------------------------------------------------------- - * - * 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( - Object *oPtr, /* The object to get the context for. */ - Tcl_Obj *methodNameObj, /* The name of the method to get the context - * for. NULL when getting a constructor or - * destructor chain. */ - int flags, /* What sort of context are we looking for. - * Only the bits PUBLIC_METHOD, CONSTRUCTOR, - * PRIVATE_METHOD, DESTRUCTOR and - * FILTER_HANDLING are useful. */ - Object *contextObj, /* Context object; when equal to oPtr, it - * means that private methods may also be - * added. [TIP 500] */ - Class *contextCls, /* Context class; the currently considered - * class is equal to this, private methods may - * also be added. [TIP 500] */ - Tcl_Obj *cacheInThisObj) /* What object to cache in, or NULL if it is - * to be in the same object as the - * methodNameObj. */ -{ - CallContext *contextPtr; - CallChain *callPtr; - struct ChainBuilder cb; - int i, count; - int doFilters, donePrivate = 0; - Tcl_HashEntry *hPtr; - Tcl_HashTable doneFilters; - - if (cacheInThisObj == NULL) { - cacheInThisObj = methodNameObj; - } - if (flags&(SPECIAL|FILTER_HANDLING) || (oPtr->flags&FILTER_HANDLING)) { - hPtr = NULL; - doFilters = 0; - - /* - * Check if we have a cached valid constructor or destructor. - */ - - if (flags & CONSTRUCTOR) { - callPtr = oPtr->selfCls->constructorChainPtr; - if ((callPtr != NULL) - && (callPtr->objectEpoch == oPtr->selfCls->thisPtr->epoch) - && (callPtr->epoch == oPtr->fPtr->epoch)) { - callPtr->refCount++; - goto returnContext; - } - } else if (flags & DESTRUCTOR) { - callPtr = oPtr->selfCls->destructorChainPtr; - if ((oPtr->mixins.num == 0) && (callPtr != NULL) - && (callPtr->objectEpoch == oPtr->selfCls->thisPtr->epoch) - && (callPtr->epoch == oPtr->fPtr->epoch)) { - callPtr->refCount++; - goto returnContext; - } - } - } else { - /* - * Check if we can get the chain out of the Tcl_Obj method name or out - * of the cache. This is made a bit more complex by the fact that - * there are multiple different layers of cache (in the Tcl_Obj, in - * the object, and in the class). - */ - - const Tcl_ObjInternalRep *irPtr; - const int reuseMask = (WANT_PUBLIC(flags) ? ~0 : ~PUBLIC_METHOD); - - if ((irPtr = TclFetchInternalRep(cacheInThisObj, &methodNameType))) { - callPtr = (CallChain *)irPtr->twoPtrValue.ptr1; - if (IsStillValid(callPtr, oPtr, flags, reuseMask)) { - callPtr->refCount++; - goto returnContext; - } - Tcl_StoreInternalRep(cacheInThisObj, &methodNameType, NULL); - } - - if (oPtr->flags & USE_CLASS_CACHE) { - if (oPtr->selfCls->classChainCache != NULL) { - hPtr = Tcl_FindHashEntry(oPtr->selfCls->classChainCache, - (char *) methodNameObj); - } else { - hPtr = NULL; - } - } else { - if (oPtr->chainCache != NULL) { - hPtr = Tcl_FindHashEntry(oPtr->chainCache, - (char *) methodNameObj); - } else { - hPtr = NULL; - } - } - - if (hPtr != NULL && Tcl_GetHashValue(hPtr) != NULL) { - callPtr = (CallChain *)Tcl_GetHashValue(hPtr); - if (IsStillValid(callPtr, oPtr, flags, reuseMask)) { - callPtr->refCount++; - goto returnContext; - } - Tcl_SetHashValue(hPtr, NULL); - TclOODeleteChain(callPtr); - } - - doFilters = 1; - } - - callPtr = (CallChain *)ckalloc(sizeof(CallChain)); - InitCallChain(callPtr, oPtr, flags); - - cb.callChainPtr = callPtr; - cb.filterLength = 0; - cb.oPtr = oPtr; - - /* - * If we're working with a forced use of unknown, do that now. - */ - - if (flags & FORCE_UNKNOWN) { - AddSimpleChainToCallContext(oPtr, NULL, - oPtr->fPtr->unknownMethodNameObj, &cb, NULL, BUILDING_MIXINS, - NULL); - AddSimpleChainToCallContext(oPtr, NULL, - oPtr->fPtr->unknownMethodNameObj, &cb, NULL, 0, NULL); - callPtr->flags |= OO_UNKNOWN_METHOD; - callPtr->epoch = -1; - if (callPtr->numChain == 0) { - TclOODeleteChain(callPtr); - return NULL; - } - goto returnContext; - } - - /* - * Add all defined filters (if any, and if we're going to be processing - * them; they're not processed for constructors, destructors or when we're - * in the middle of processing a filter). - */ - - if (doFilters) { - Tcl_Obj *filterObj; - Class *mixinPtr; - - doFilters = 1; - Tcl_InitObjHashTable(&doneFilters); - FOREACH(mixinPtr, oPtr->mixins) { - AddClassFiltersToCallContext(oPtr, mixinPtr, &cb, &doneFilters, - TRAVERSED_MIXIN|BUILDING_MIXINS|OBJECT_MIXIN); - AddClassFiltersToCallContext(oPtr, mixinPtr, &cb, &doneFilters, - OBJECT_MIXIN); - } - FOREACH(filterObj, oPtr->filters) { - donePrivate |= AddSimpleChainToCallContext(oPtr, contextCls, - filterObj, &cb, &doneFilters, BUILDING_MIXINS, NULL); - donePrivate |= AddSimpleChainToCallContext(oPtr, contextCls, - filterObj, &cb, &doneFilters, 0, NULL); - } - AddClassFiltersToCallContext(oPtr, oPtr->selfCls, &cb, &doneFilters, - BUILDING_MIXINS); - AddClassFiltersToCallContext(oPtr, oPtr->selfCls, &cb, &doneFilters, - 0); - Tcl_DeleteHashTable(&doneFilters); - } - count = cb.filterLength = callPtr->numChain; - - /* - * Add the actual method implementations. We have to do this twice to - * handle class mixins right. - */ - - if (oPtr == contextObj) { - donePrivate |= AddInstancePrivateToCallContext(oPtr, methodNameObj, - &cb, flags); - donePrivate |= (contextObj->flags & HAS_PRIVATE_METHODS); - } - donePrivate |= AddSimpleChainToCallContext(oPtr, contextCls, - methodNameObj, &cb, NULL, flags|BUILDING_MIXINS, NULL); - donePrivate |= AddSimpleChainToCallContext(oPtr, contextCls, - methodNameObj, &cb, NULL, flags, NULL); - - /* - * Check to see if the method has no implementation. If so, we probably - * need to add in a call to the unknown method. Otherwise, set up the - * cacheing of the method implementation (if relevant). - */ - - if (count == callPtr->numChain) { - /* - * Method does not actually exist. If we're dealing with constructors - * or destructors, this isn't a problem. - */ - - if (flags & SPECIAL) { - TclOODeleteChain(callPtr); - return NULL; - } - AddSimpleChainToCallContext(oPtr, NULL, - oPtr->fPtr->unknownMethodNameObj, &cb, NULL, BUILDING_MIXINS, - NULL); - AddSimpleChainToCallContext(oPtr, NULL, - oPtr->fPtr->unknownMethodNameObj, &cb, NULL, 0, NULL); - callPtr->flags |= OO_UNKNOWN_METHOD; - callPtr->epoch = -1; - if (count == callPtr->numChain) { - TclOODeleteChain(callPtr); - return NULL; - } - } else if (doFilters && !donePrivate) { - if (hPtr == NULL) { - if (oPtr->flags & USE_CLASS_CACHE) { - if (oPtr->selfCls->classChainCache == NULL) { - oPtr->selfCls->classChainCache = - (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); - - Tcl_InitObjHashTable(oPtr->selfCls->classChainCache); - } - hPtr = Tcl_CreateHashEntry(oPtr->selfCls->classChainCache, - (char *) methodNameObj, &i); - } else { - if (oPtr->chainCache == NULL) { - oPtr->chainCache = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); - - Tcl_InitObjHashTable(oPtr->chainCache); - } - hPtr = Tcl_CreateHashEntry(oPtr->chainCache, - (char *) methodNameObj, &i); - } - } - callPtr->refCount++; - Tcl_SetHashValue(hPtr, callPtr); - StashCallChain(cacheInThisObj, callPtr); - } else if (flags & CONSTRUCTOR) { - if (oPtr->selfCls->constructorChainPtr) { - TclOODeleteChain(oPtr->selfCls->constructorChainPtr); - } - oPtr->selfCls->constructorChainPtr = callPtr; - callPtr->refCount++; - } else if ((flags & DESTRUCTOR) && oPtr->mixins.num == 0) { - if (oPtr->selfCls->destructorChainPtr) { - TclOODeleteChain(oPtr->selfCls->destructorChainPtr); - } - oPtr->selfCls->destructorChainPtr = callPtr; - callPtr->refCount++; - } - - returnContext: - contextPtr = (CallContext *)TclStackAlloc(oPtr->fPtr->interp, sizeof(CallContext)); - contextPtr->oPtr = oPtr; - - /* - * Corresponding TclOODecrRefCount() in TclOODeleteContext - */ - - AddRef(oPtr); - contextPtr->callPtr = callPtr; - contextPtr->skip = 2; - contextPtr->index = 0; - return contextPtr; -} - -/* - * ---------------------------------------------------------------------- - * - * TclOOGetStereotypeCallChain -- - * - * Construct a call-chain for a method that would be used by a - * stereotypical instance of the given class (i.e., where the object has - * no definitions special to itself). - * - * ---------------------------------------------------------------------- - */ - -CallChain * -TclOOGetStereotypeCallChain( - Class *clsPtr, /* The object to get the context for. */ - Tcl_Obj *methodNameObj, /* The name of the method to get the context - * for. NULL when getting a constructor or - * destructor chain. */ - int flags) /* What sort of context are we looking for. - * Only the bits PUBLIC_METHOD, CONSTRUCTOR, - * PRIVATE_METHOD, DESTRUCTOR and - * FILTER_HANDLING are useful. */ -{ - CallChain *callPtr; - struct ChainBuilder cb; - int i, count; - Foundation *fPtr = clsPtr->thisPtr->fPtr; - Tcl_HashEntry *hPtr; - Tcl_HashTable doneFilters; - Object obj; - - /* - * Synthesize a temporary stereotypical object so that we can use existing - * machinery to produce the stereotypical call chain. - */ - - memset(&obj, 0, sizeof(Object)); - obj.fPtr = fPtr; - obj.selfCls = clsPtr; - obj.refCount = 1; - obj.flags = USE_CLASS_CACHE; - - /* - * Check if we can get the chain out of the Tcl_Obj method name or out of - * the cache. This is made a bit more complex by the fact that there are - * multiple different layers of cache (in the Tcl_Obj, in the object, and - * in the class). - */ - - if (clsPtr->classChainCache != NULL) { - hPtr = Tcl_FindHashEntry(clsPtr->classChainCache, - (char *) methodNameObj); - if (hPtr != NULL && Tcl_GetHashValue(hPtr) != NULL) { - const int reuseMask = (WANT_PUBLIC(flags) ? ~0 : ~PUBLIC_METHOD); - - callPtr = (CallChain *)Tcl_GetHashValue(hPtr); - if (IsStillValid(callPtr, &obj, flags, reuseMask)) { - callPtr->refCount++; - return callPtr; - } - Tcl_SetHashValue(hPtr, NULL); - TclOODeleteChain(callPtr); - } - } else { - hPtr = NULL; - } - - callPtr = (CallChain *)ckalloc(sizeof(CallChain)); - memset(callPtr, 0, sizeof(CallChain)); - callPtr->flags = flags & (PUBLIC_METHOD|PRIVATE_METHOD|FILTER_HANDLING); - callPtr->epoch = fPtr->epoch; - callPtr->objectCreationEpoch = fPtr->tsdPtr->nsCount; - callPtr->objectEpoch = clsPtr->thisPtr->epoch; - callPtr->refCount = 1; - callPtr->chain = callPtr->staticChain; - - cb.callChainPtr = callPtr; - cb.filterLength = 0; - cb.oPtr = &obj; - - /* - * Add all defined filters (if any, and if we're going to be processing - * them; they're not processed for constructors, destructors or when we're - * in the middle of processing a filter). - */ - - Tcl_InitObjHashTable(&doneFilters); - AddClassFiltersToCallContext(&obj, clsPtr, &cb, &doneFilters, - BUILDING_MIXINS); - AddClassFiltersToCallContext(&obj, clsPtr, &cb, &doneFilters, 0); - Tcl_DeleteHashTable(&doneFilters); - count = cb.filterLength = callPtr->numChain; - - /* - * Add the actual method implementations. - */ - - AddSimpleChainToCallContext(&obj, NULL, methodNameObj, &cb, NULL, - flags|BUILDING_MIXINS, NULL); - AddSimpleChainToCallContext(&obj, NULL, methodNameObj, &cb, NULL, flags, - NULL); - - /* - * Check to see if the method has no implementation. If so, we probably - * need to add in a call to the unknown method. Otherwise, set up the - * caching of the method implementation (if relevant). - */ - - if (count == callPtr->numChain) { - AddSimpleChainToCallContext(&obj, NULL, fPtr->unknownMethodNameObj, - &cb, NULL, BUILDING_MIXINS, NULL); - AddSimpleChainToCallContext(&obj, NULL, fPtr->unknownMethodNameObj, - &cb, NULL, 0, NULL); - callPtr->flags |= OO_UNKNOWN_METHOD; - callPtr->epoch = -1; - if (count == callPtr->numChain) { - TclOODeleteChain(callPtr); - return NULL; - } - } else { - if (hPtr == NULL) { - if (clsPtr->classChainCache == NULL) { - clsPtr->classChainCache = (Tcl_HashTable *)ckalloc(sizeof(Tcl_HashTable)); - Tcl_InitObjHashTable(clsPtr->classChainCache); - } - hPtr = Tcl_CreateHashEntry(clsPtr->classChainCache, - (char *) methodNameObj, &i); - } - callPtr->refCount++; - Tcl_SetHashValue(hPtr, callPtr); - StashCallChain(methodNameObj, callPtr); - } - return callPtr; -} - -/* - * ---------------------------------------------------------------------- - * - * AddClassFiltersToCallContext -- - * - * Logic to make extracting all the filters from the class context much - * easier. - * - * ---------------------------------------------------------------------- - */ - -static void -AddClassFiltersToCallContext( - Object *const oPtr, /* Object that the filters operate on. */ - Class *clsPtr, /* Class to get the filters from. */ - struct ChainBuilder *const cbPtr, - /* Context to fill with call chain entries. */ - Tcl_HashTable *const doneFilters, - /* Where to record what filters have been - * processed. Keys are objects, values are - * ignored. */ - int flags) /* Whether we've gone along a mixin link - * yet. */ -{ - int i; - int clearedFlags = - flags & ~(TRAVERSED_MIXIN|OBJECT_MIXIN|BUILDING_MIXINS); - Class *superPtr, *mixinPtr; - Tcl_Obj *filterObj; - - tailRecurse: - if (clsPtr == NULL) { - return; - } - - /* - * Add all the filters defined by classes mixed into the main class - * hierarchy. - */ - - FOREACH(mixinPtr, clsPtr->mixins) { - AddClassFiltersToCallContext(oPtr, mixinPtr, cbPtr, doneFilters, - flags|TRAVERSED_MIXIN); - } - - /* - * Add all the class filters from the current class. Note that the filters - * are added starting at the object root, as this allows the object to - * override how filters work to extend their behaviour. - */ - - if (MIXIN_CONSISTENT(flags)) { - FOREACH(filterObj, clsPtr->filters) { - int isNew; - - (void) Tcl_CreateHashEntry(doneFilters, (char *) filterObj, - &isNew); - if (isNew) { - AddSimpleChainToCallContext(oPtr, NULL, filterObj, cbPtr, - doneFilters, clearedFlags|BUILDING_MIXINS, clsPtr); - AddSimpleChainToCallContext(oPtr, NULL, filterObj, cbPtr, - doneFilters, clearedFlags, clsPtr); - } - } - } - - /* - * Now process the recursive case. Notice the tail-call optimization. - */ - - switch (clsPtr->superclasses.num) { - case 1: - clsPtr = clsPtr->superclasses.list[0]; - goto tailRecurse; - default: - FOREACH(superPtr, clsPtr->superclasses) { - AddClassFiltersToCallContext(oPtr, superPtr, cbPtr, doneFilters, - flags); - } - case 0: - return; - } -} - -/* - * ---------------------------------------------------------------------- - * - * AddPrivatesFromClassChainToCallContext -- - * - * Helper for AddSimpleChainToCallContext that is used to find private - * methds and add them to the call chain. Returns true when a private - * method is found and added. [TIP 500] - * - * ---------------------------------------------------------------------- - */ - -static int -AddPrivatesFromClassChainToCallContext( - Class *classPtr, /* Class to add the call chain entries for. */ - Class *const contextCls, /* Context class; the currently considered - * class is equal to this, private methods may - * also be added. */ - Tcl_Obj *const methodName, /* Name of method to add the call chain - * entries for. */ - struct ChainBuilder *const cbPtr, - /* Where to add the call chain entries. */ - Tcl_HashTable *const doneFilters, - /* Where to record what call chain entries - * have been processed. */ - int flags, /* What sort of call chain are we building. */ - Class *const filterDecl) /* The class that declared the filter. If - * NULL, either the filter was declared by the - * object or this isn't a filter. */ -{ - int i; - Class *superPtr; - - /* - * We hard-code the tail-recursive form. It's by far the most common case - * *and* it is much more gentle on the stack. - * - * Note that mixins must be processed before the main class hierarchy. - * [Bug 1998221] - */ - - tailRecurse: - FOREACH(superPtr, classPtr->mixins) { - if (AddPrivatesFromClassChainToCallContext(superPtr, contextCls, - methodName, cbPtr, doneFilters, flags|TRAVERSED_MIXIN, - filterDecl)) { - return 1; - } - } - - if (classPtr == contextCls) { - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&classPtr->classMethods, - methodName); - - if (hPtr != NULL) { - Method *mPtr = (Method *)Tcl_GetHashValue(hPtr); - - if (IS_PRIVATE(mPtr)) { - AddMethodToCallChain(mPtr, cbPtr, doneFilters, filterDecl, - flags); - return 1; - } - } - } - - switch (classPtr->superclasses.num) { - case 1: - classPtr = classPtr->superclasses.list[0]; - goto tailRecurse; - default: - FOREACH(superPtr, classPtr->superclasses) { - if (AddPrivatesFromClassChainToCallContext(superPtr, contextCls, - methodName, cbPtr, doneFilters, flags, filterDecl)) { - return 1; - } - } - /* FALLTHRU */ - case 0: - return 0; - } -} - -/* - * ---------------------------------------------------------------------- - * - * AddSimpleClassChainToCallContext -- - * - * Construct a call-chain from a class hierarchy. - * - * ---------------------------------------------------------------------- - */ - -static int -AddSimpleClassChainToCallContext( - Class *classPtr, /* Class to add the call chain entries for. */ - Tcl_Obj *const methodNameObj, - /* Name of method to add the call chain - * entries for. */ - struct ChainBuilder *const cbPtr, - /* Where to add the call chain entries. */ - Tcl_HashTable *const doneFilters, - /* Where to record what call chain entries - * have been processed. */ - int flags, /* What sort of call chain are we building. */ - Class *const filterDecl) /* The class that declared the filter. If - * NULL, either the filter was declared by the - * object or this isn't a filter. */ -{ - int i, privateDanger = 0; - Class *superPtr; - - /* - * We hard-code the tail-recursive form. It's by far the most common case - * *and* it is much more gentle on the stack. - * - * Note that mixins must be processed before the main class hierarchy. - * [Bug 1998221] - */ - - tailRecurse: - FOREACH(superPtr, classPtr->mixins) { - privateDanger |= AddSimpleClassChainToCallContext(superPtr, - methodNameObj, cbPtr, doneFilters, flags | TRAVERSED_MIXIN, - filterDecl); - } - - if (flags & CONSTRUCTOR) { - AddMethodToCallChain(classPtr->constructorPtr, cbPtr, doneFilters, - filterDecl, flags); - } else if (flags & DESTRUCTOR) { - AddMethodToCallChain(classPtr->destructorPtr, cbPtr, doneFilters, - filterDecl, flags); - } else { - Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&classPtr->classMethods, - (char *) methodNameObj); - - if (classPtr->flags & HAS_PRIVATE_METHODS) { - privateDanger |= 1; - } - if (hPtr != NULL) { - Method *mPtr = (Method *)Tcl_GetHashValue(hPtr); - - if (!IS_PRIVATE(mPtr)) { - if (!(flags & KNOWN_STATE)) { - if (flags & PUBLIC_METHOD) { - if (!IS_PUBLIC(mPtr)) { - return privateDanger; - } - flags |= DEFINITE_PUBLIC; - } else { - flags |= DEFINITE_PROTECTED; - } - } - AddMethodToCallChain(mPtr, cbPtr, doneFilters, filterDecl, - flags); - } - } - } - - switch (classPtr->superclasses.num) { - case 1: - classPtr = classPtr->superclasses.list[0]; - goto tailRecurse; - default: - FOREACH(superPtr, classPtr->superclasses) { - privateDanger |= AddSimpleClassChainToCallContext(superPtr, - methodNameObj, cbPtr, doneFilters, flags, filterDecl); - } - /* FALLTHRU */ - case 0: - return privateDanger; - } -} - -/* - * ---------------------------------------------------------------------- - * - * TclOORenderCallChain -- - * - * Create a description of a call chain. Used in [info object call], - * [info class call], and [self call]. - * - * ---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclOORenderCallChain( - Tcl_Interp *interp, - CallChain *callPtr) -{ - Tcl_Obj *filterLiteral, *methodLiteral, *objectLiteral, *privateLiteral; - Tcl_Obj *resultObj, *descObjs[4], **objv; - Foundation *fPtr = TclOOGetFoundation(interp); - int i; - - /* - * Allocate the literals (potentially) used in our description. - */ - - TclNewLiteralStringObj(filterLiteral, "filter"); - Tcl_IncrRefCount(filterLiteral); - TclNewLiteralStringObj(methodLiteral, "method"); - Tcl_IncrRefCount(methodLiteral); - TclNewLiteralStringObj(objectLiteral, "object"); - Tcl_IncrRefCount(objectLiteral); - TclNewLiteralStringObj(privateLiteral, "private"); - Tcl_IncrRefCount(privateLiteral); - - /* - * 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). - */ - - objv = (Tcl_Obj **)TclStackAlloc(interp, callPtr->numChain * sizeof(Tcl_Obj *)); - for (i = 0 ; i < callPtr->numChain ; i++) { - struct MInvoke *miPtr = &callPtr->chain[i]; - - descObjs[0] = - miPtr->isFilter ? filterLiteral : - callPtr->flags & OO_UNKNOWN_METHOD ? fPtr->unknownMethodNameObj : - IS_PRIVATE(miPtr->mPtr) ? privateLiteral : - methodLiteral; - descObjs[1] = - callPtr->flags & CONSTRUCTOR ? fPtr->constructorName : - callPtr->flags & DESTRUCTOR ? fPtr->destructorName : - miPtr->mPtr->namePtr; - descObjs[2] = miPtr->mPtr->declaringClassPtr - ? Tcl_GetObjectName(interp, - (Tcl_Object) miPtr->mPtr->declaringClassPtr->thisPtr) - : objectLiteral; - descObjs[3] = Tcl_NewStringObj(miPtr->mPtr->typePtr->name, -1); - - objv[i] = Tcl_NewListObj(4, descObjs); - } - - /* - * Drop the local references to the literals; if they're actually used, - * they'll live on the description itself. - */ - - Tcl_DecrRefCount(filterLiteral); - Tcl_DecrRefCount(methodLiteral); - Tcl_DecrRefCount(objectLiteral); - Tcl_DecrRefCount(privateLiteral); - - /* - * Finish building the description and return it. - */ - - resultObj = Tcl_NewListObj(callPtr->numChain, objv); - TclStackFree(interp, objv); - return resultObj; -} - -/* - * ---------------------------------------------------------------------- - * - * TclOOGetDefineContextNamespace -- - * - * Responsible for determining which namespace to use for definitions. - * This is done by building a define chain, which models (strongly!) the - * way that a call chain works but with a different internal model. - * - * Then it walks the chain to find the first namespace name that actually - * resolves to an existing namespace. - * - * Returns: - * Name of namespace, or NULL if none can be found. Note that this - * function does *not* set an error message in the interpreter on failure. - * - * ---------------------------------------------------------------------- - */ - -#define DEFINE_CHAIN_STATIC_SIZE 4 /* Enough space to store most cases. */ - -Tcl_Namespace * -TclOOGetDefineContextNamespace( - Tcl_Interp *interp, /* In what interpreter should namespace names - * actually be resolved. */ - Object *oPtr, /* The object to get the context for. */ - int forClass) /* What sort of context are we looking for. - * If true, we are going to use this for - * [oo::define], otherwise, we are going to - * use this for [oo::objdefine]. */ -{ - DefineChain define; - DefineEntry staticSpace[DEFINE_CHAIN_STATIC_SIZE]; - DefineEntry *entryPtr; - Tcl_Namespace *nsPtr = NULL; - int i, flags = (forClass ? DEFINE_FOR_CLASS : 0); - - define.list = staticSpace; - define.num = 0; - define.size = DEFINE_CHAIN_STATIC_SIZE; - - /* - * Add the actual define locations. We have to do this twice to handle - * class mixins right. - */ - - AddSimpleDefineNamespaces(oPtr, &define, flags | BUILDING_MIXINS); - AddSimpleDefineNamespaces(oPtr, &define, flags); - - /* - * Go through the list until we find a namespace whose name we can - * resolve. - */ - - FOREACH_STRUCT(entryPtr, define) { - if (TclGetNamespaceFromObj(interp, entryPtr->namespaceName, - &nsPtr) == TCL_OK) { - break; - } - Tcl_ResetResult(interp); - } - if (define.list != staticSpace) { - ckfree(define.list); - } - return nsPtr; -} - -/* - * ---------------------------------------------------------------------- - * - * AddSimpleDefineNamespaces -- - * - * Adds to the definition chain all the definitions provided by an - * object's class and its mixins, taking into account everything they - * inherit from. - * - * ---------------------------------------------------------------------- - */ - -static inline void -AddSimpleDefineNamespaces( - Object *const oPtr, /* Object to add define chain entries for. */ - DefineChain *const definePtr, - /* Where to add the define chain entries. */ - int flags) /* What sort of define chain are we - * building. */ -{ - Class *mixinPtr; - int i; - - FOREACH(mixinPtr, oPtr->mixins) { - AddSimpleClassDefineNamespaces(mixinPtr, definePtr, - flags | TRAVERSED_MIXIN); - } - - AddSimpleClassDefineNamespaces(oPtr->selfCls, definePtr, flags); -} - -/* - * ---------------------------------------------------------------------- - * - * AddSimpleClassDefineNamespaces -- - * - * Adds to the definition chain all the definitions provided by a class - * and its superclasses and its class mixins. - * - * ---------------------------------------------------------------------- - */ - -static void -AddSimpleClassDefineNamespaces( - Class *classPtr, /* Class to add the define chain entries for. */ - DefineChain *const definePtr, - /* Where to add the define chain entries. */ - int flags) /* What sort of define chain are we - * building. */ -{ - int i; - Class *superPtr; - - /* - * We hard-code the tail-recursive form. It's by far the most common case - * *and* it is much more gentle on the stack. - */ - - tailRecurse: - FOREACH(superPtr, classPtr->mixins) { - AddSimpleClassDefineNamespaces(superPtr, definePtr, - flags | TRAVERSED_MIXIN); - } - - if (flags & DEFINE_FOR_CLASS) { - AddDefinitionNamespaceToChain(classPtr, classPtr->clsDefinitionNs, - definePtr, flags); - } else { - AddDefinitionNamespaceToChain(classPtr, classPtr->objDefinitionNs, - definePtr, flags); - } - - switch (classPtr->superclasses.num) { - case 1: - classPtr = classPtr->superclasses.list[0]; - goto tailRecurse; - default: - FOREACH(superPtr, classPtr->superclasses) { - AddSimpleClassDefineNamespaces(superPtr, definePtr, flags); - } - case 0: - return; - } -} - -/* - * ---------------------------------------------------------------------- - * - * AddDefinitionNamespaceToChain -- - * - * Adds a single item to the definition chain (if it is meaningful), - * reallocating the space for the chain if necessary. - * - * ---------------------------------------------------------------------- - */ - -static inline void -AddDefinitionNamespaceToChain( - Class *const definerCls, /* What class defines this entry. */ - Tcl_Obj *const namespaceName, /* The name for this entry (or NULL, a - * no-op). */ - DefineChain *const definePtr, - /* The define chain to add the method - * implementation to. */ - int flags) /* Used to check if we're mixin-consistent - * only. Mixin-consistent means that either - * we're looking to add things from a mixin - * and we have passed a mixin, or we're not - * looking to add things from a mixin and have - * not passed a mixin. */ -{ - int i; - - /* - * Return if this entry is blank. This is also where we enforce - * mixin-consistency. - */ - - if (namespaceName == NULL || !MIXIN_CONSISTENT(flags)) { - return; - } - - /* - * First test whether the method is already in the call chain. - */ - - for (i=0 ; i<definePtr->num ; i++) { - if (definePtr->list[i].definerCls == definerCls) { - /* - * 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. - * - * We skip changing anything if the place we found was already at - * the end of the list. - */ - - if (i < definePtr->num - 1) { - memmove(&definePtr->list[i], &definePtr->list[i + 1], - sizeof(DefineEntry) * (definePtr->num - i - 1)); - definePtr->list[i].definerCls = definerCls; - definePtr->list[i].namespaceName = namespaceName; - } - return; - } - } - - /* - * Need to really add the define. This is made a bit more complex by the - * fact that we are using some "static" space initially, and only start - * realloc-ing if the chain gets long. - */ - - if (definePtr->num == definePtr->size) { - definePtr->size *= 2; - if (definePtr->num == DEFINE_CHAIN_STATIC_SIZE) { - DefineEntry *staticList = definePtr->list; - - definePtr->list = - (DefineEntry *)ckalloc(sizeof(DefineEntry) * definePtr->size); - memcpy(definePtr->list, staticList, - sizeof(DefineEntry) * definePtr->num); - } else { - definePtr->list = (DefineEntry *)ckrealloc(definePtr->list, - sizeof(DefineEntry) * definePtr->size); - } - } - definePtr->list[i].definerCls = definerCls; - definePtr->list[i].namespaceName = namespaceName; - definePtr->num++; -} - -/* - * ---------------------------------------------------------------------- - * - * FindClassProps -- - * - * Discover the properties known to a class and its superclasses. - * The property names become the keys in the accumulator hash table - * (which is used as a set). - * - * ---------------------------------------------------------------------- - */ - -static void -FindClassProps( - Class *clsPtr, /* The object to inspect. Must exist. */ - int writable, /* Whether we're after the readable or writable - * property set. */ - Tcl_HashTable *accumulator) /* Where to gather the names. */ -{ - int i, dummy; - Tcl_Obj *propName; - Class *mixin, *sup; - - tailRecurse: - if (writable) { - FOREACH(propName, clsPtr->properties.writable) { - Tcl_CreateHashEntry(accumulator, (void *) propName, &dummy); - } - } else { - FOREACH(propName, clsPtr->properties.readable) { - Tcl_CreateHashEntry(accumulator, (void *) propName, &dummy); - } - } - if (clsPtr->thisPtr->flags & ROOT_OBJECT) { - /* - * We do *not* traverse upwards from the root! - */ - return; - } - FOREACH(mixin, clsPtr->mixins) { - FindClassProps(mixin, writable, accumulator); - } - if (clsPtr->superclasses.num == 1) { - clsPtr = clsPtr->superclasses.list[0]; - goto tailRecurse; - } - FOREACH(sup, clsPtr->superclasses) { - FindClassProps(sup, writable, accumulator); - } -} - -/* - * ---------------------------------------------------------------------- - * - * FindObjectProps -- - * - * Discover the properties known to an object and all its classes. - * The property names become the keys in the accumulator hash table - * (which is used as a set). - * - * ---------------------------------------------------------------------- - */ - -static void -FindObjectProps( - Object *oPtr, /* The object to inspect. Must exist. */ - int writable, /* Whether we're after the readable or writable - * property set. */ - Tcl_HashTable *accumulator) /* Where to gather the names. */ -{ - int i, dummy; - Tcl_Obj *propName; - Class *mixin; - - if (writable) { - FOREACH(propName, oPtr->properties.writable) { - Tcl_CreateHashEntry(accumulator, (void *) propName, &dummy); - } - } else { - FOREACH(propName, oPtr->properties.readable) { - Tcl_CreateHashEntry(accumulator, (void *) propName, &dummy); - } - } - FOREACH(mixin, oPtr->mixins) { - FindClassProps(mixin, writable, accumulator); - } - FindClassProps(oPtr->selfCls, writable, accumulator); -} - -/* - * ---------------------------------------------------------------------- - * - * TclOOGetAllClassProperties -- - * - * Get the list of all properties known to a class, including to its - * superclasses. Manages a cache so this operation is usually cheap. - * The order of properties in the resulting list is undefined. - * - * ---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclOOGetAllClassProperties( - Class *clsPtr, /* The class to inspect. Must exist. */ - int writable, /* Whether to get writable properties. If - * false, readable properties will be returned - * instead. */ - int *allocated) /* Address of variable to set to true if a - * Tcl_Obj was allocated and may be safely - * modified by the caller. */ -{ - Tcl_HashTable hashTable; - FOREACH_HASH_DECLS; - Tcl_Obj *propName, *result; - void *dummy; - - /* - * Look in the cache. - */ - - if (clsPtr->properties.epoch == clsPtr->thisPtr->fPtr->epoch) { - if (writable) { - if (clsPtr->properties.allWritableCache) { - *allocated = 0; - return clsPtr->properties.allWritableCache; - } - } else { - if (clsPtr->properties.allReadableCache) { - *allocated = 0; - return clsPtr->properties.allReadableCache; - } - } - } - - /* - * Gather the information. Unsorted! (Caller will sort.) - */ - - *allocated = 1; - Tcl_InitObjHashTable(&hashTable); - FindClassProps(clsPtr, writable, &hashTable); - TclNewObj(result); - FOREACH_HASH(propName, dummy, &hashTable) { - Tcl_ListObjAppendElement(NULL, result, propName); - } - Tcl_DeleteHashTable(&hashTable); - - /* - * Cache the information. Also purges the cache. - */ - - if (clsPtr->properties.epoch != clsPtr->thisPtr->fPtr->epoch) { - if (clsPtr->properties.allWritableCache) { - Tcl_DecrRefCount(clsPtr->properties.allWritableCache); - clsPtr->properties.allWritableCache = NULL; - } - if (clsPtr->properties.allReadableCache) { - Tcl_DecrRefCount(clsPtr->properties.allReadableCache); - clsPtr->properties.allReadableCache = NULL; - } - } - clsPtr->properties.epoch = clsPtr->thisPtr->fPtr->epoch; - if (writable) { - clsPtr->properties.allWritableCache = result; - } else { - clsPtr->properties.allReadableCache = result; - } - Tcl_IncrRefCount(result); - return result; -} - -/* - * ---------------------------------------------------------------------- - * - * TclOOGetAllObjectProperties -- - * - * Get the list of all properties known to a object, including to its - * classes. Manages a cache so this operation is usually cheap. - * The order of properties in the resulting list is undefined. - * - * ---------------------------------------------------------------------- - */ - -Tcl_Obj * -TclOOGetAllObjectProperties( - Object *oPtr, /* The object to inspect. Must exist. */ - int writable, /* Whether to get writable properties. If - * false, readable properties will be returned - * instead. */ - int *allocated) /* Address of variable to set to true if a - * Tcl_Obj was allocated and may be safely - * modified by the caller. */ -{ - Tcl_HashTable hashTable; - FOREACH_HASH_DECLS; - Tcl_Obj *propName, *result; - void *dummy; - - /* - * Look in the cache. - */ - - if (oPtr->properties.epoch == oPtr->fPtr->epoch) { - if (writable) { - if (oPtr->properties.allWritableCache) { - *allocated = 0; - return oPtr->properties.allWritableCache; - } - } else { - if (oPtr->properties.allReadableCache) { - *allocated = 0; - return oPtr->properties.allReadableCache; - } - } - } - - /* - * Gather the information. Unsorted! (Caller will sort.) - */ - - *allocated = 1; - Tcl_InitObjHashTable(&hashTable); - FindObjectProps(oPtr, writable, &hashTable); - TclNewObj(result); - FOREACH_HASH(propName, dummy, &hashTable) { - Tcl_ListObjAppendElement(NULL, result, propName); - } - Tcl_DeleteHashTable(&hashTable); - - /* - * Cache the information. - */ - - if (oPtr->properties.epoch != oPtr->fPtr->epoch) { - if (oPtr->properties.allWritableCache) { - Tcl_DecrRefCount(oPtr->properties.allWritableCache); - oPtr->properties.allWritableCache = NULL; - } - if (oPtr->properties.allReadableCache) { - Tcl_DecrRefCount(oPtr->properties.allReadableCache); - oPtr->properties.allReadableCache = NULL; - } - } - oPtr->properties.epoch = oPtr->fPtr->epoch; - if (writable) { - oPtr->properties.allWritableCache = result; - } else { - oPtr->properties.allReadableCache = result; - } - Tcl_IncrRefCount(result); - return result; -} - -/* - * Local Variables: - * mode: c - * c-basic-offset: 4 - * fill-column: 78 - * End: - */ |
