diff options
Diffstat (limited to 'tcl8.6/pkgs/itcl4.1.2/generic/itclClass.c')
-rw-r--r-- | tcl8.6/pkgs/itcl4.1.2/generic/itclClass.c | 2639 |
1 files changed, 2639 insertions, 0 deletions
diff --git a/tcl8.6/pkgs/itcl4.1.2/generic/itclClass.c b/tcl8.6/pkgs/itcl4.1.2/generic/itclClass.c new file mode 100644 index 0000000..7295751 --- /dev/null +++ b/tcl8.6/pkgs/itcl4.1.2/generic/itclClass.c @@ -0,0 +1,2639 @@ +/* + * ------------------------------------------------------------------------ + * PACKAGE: [incr Tcl] + * DESCRIPTION: Object-Oriented Extensions to Tcl + * + * [incr Tcl] provides object-oriented extensions to Tcl, much as + * C++ provides object-oriented extensions to C. It provides a means + * of encapsulating related procedures together with their shared data + * in a local namespace that is hidden from the outside world. It + * promotes code re-use through inheritance. More than anything else, + * it encourages better organization of Tcl applications through the + * object-oriented paradigm, leading to code that is easier to + * understand and maintain. + * + * These procedures handle class definitions. Classes are composed of + * data members (public/protected/common) and the member functions + * (methods/procs) that operate on them. Each class has its own + * namespace which manages the class scope. + * + * ======================================================================== + * AUTHOR: Michael J. McLennan + * Bell Labs Innovations for Lucent Technologies + * mmclennan@lucent.com + * http://www.tcltk.com/itcl + * + * overhauled version author: Arnulf Wiedemann Copyright (c) 2007 + * ======================================================================== + * Copyright (c) 1993-1998 Lucent Technologies, Inc. + * ------------------------------------------------------------------------ + * See the file "license.terms" for information on usage and redistribution + * of this file, and for a DISCLAIMER OF ALL WARRANTIES. + */ +#include "itclInt.h" + +static Tcl_NamespaceDeleteProc* _TclOONamespaceDeleteProc = NULL; +static void ItclDeleteOption(char *cdata); + +/* + * FORWARD DECLARATIONS + */ +static void ItclDestroyClass(ClientData cdata); +static void ItclFreeClass (char* cdata); +static void ItclDeleteFunction(ItclMemberFunc *imPtr); +static void ItclDeleteComponent(ItclComponent *icPtr); +static void ItclDeleteOption(char *cdata); + +void +ItclPreserveClass( + ItclClass *iclsPtr) +{ + iclsPtr->refCount++; +} + +void +ItclReleaseClass( + ClientData clientData) +{ + ItclClass *iclsPtr = (ItclClass *)clientData; + + if (--iclsPtr->refCount == 0) { + ItclFreeClass((char *) clientData); + } +} + +/* + * ------------------------------------------------------------------------ + * Itcl_DeleteMemberFunc() + * + * ------------------------------------------------------------------------ + */ + +void Itcl_DeleteMemberFunc ( + char *cdata) +{ + /* needed for stubs compatibility */ + ItclMemberFunc *imPtr; + + imPtr = (ItclMemberFunc *)cdata; + if (imPtr == NULL) { + /* FIXME need code here */ + } + ItclDeleteFunction((ItclMemberFunc *)cdata); +} + +/* + * ------------------------------------------------------------------------ + * ItclDestroyClass2() + * + * ------------------------------------------------------------------------ + */ + +static void +ItclDestroyClass2( + ClientData clientData) /* The class being deleted. */ +{ + ItclClass *iclsPtr; + + iclsPtr = clientData; + ItclDestroyClassNamesp(iclsPtr); + ItclReleaseClass(iclsPtr); +} + +/* + * ------------------------------------------------------------------------ + * ClassCmdDeleteTrace() + * + * ------------------------------------------------------------------------ + */ + +static void +ClassCmdDeleteTrace( + ClientData clientData, /* The class being deleted. */ + Tcl_Interp *interp, /* The interpreter containing the object. */ + const char *oldName, /* What the object was (last) called. */ + const char *newName, /* Always NULL. */ + int flags) /* Why was the object deleted? */ +{ + Tcl_HashEntry *hPtr; + Tcl_DString buffer; + Tcl_Namespace *nsPtr; + ItclObjectInfo *infoPtr; + ItclClass *iclsPtr = clientData; + + /* + * How is it decided what cleanup is done here tracing the access command deletion, + * versus what cleanup is done by the Tcl_CmdDeleteProc tied to the access command? + */ + + infoPtr = Tcl_GetAssocData(interp, ITCL_INTERP_DATA, NULL); + hPtr = Tcl_FindHashEntry(&infoPtr->classes, (char *)iclsPtr); + if (hPtr == NULL) { + return; + } + if (iclsPtr->flags & ITCL_CLASS_IS_RENAMED) { /* DUMB! name for this flag */ + return; /* Flag very likely serves no purpose as well. */ + } + iclsPtr->flags |= ITCL_CLASS_IS_RENAMED; /* DUMB! name for this flag */ + ItclPreserveClass(iclsPtr); + /* delete the namespace for the common variables */ + Tcl_DStringInit(&buffer); + Tcl_DStringAppend(&buffer, ITCL_VARIABLES_NAMESPACE, -1); + Tcl_DStringAppend(&buffer, + (Tcl_GetObjectNamespace(iclsPtr->oPtr))->fullName, -1); + nsPtr = Tcl_FindNamespace(interp, Tcl_DStringValue(&buffer), NULL, 0); + Tcl_DStringFree(&buffer); + if (nsPtr != NULL) { + Tcl_DeleteNamespace(nsPtr); + } + if (!(iclsPtr->flags & ITCL_CLASS_NS_IS_DESTROYED)) { + ItclDestroyClassNamesp(iclsPtr); + } + ItclReleaseClass(iclsPtr); + return; +} + + +/* + * ------------------------------------------------------------------------ + * ItclDeleteClassMetadata() + * + * Delete the metadata data if any + *------------------------------------------------------------------------- + */ +void +ItclDeleteClassMetadata( + ClientData clientData) +{ + /* + * This is how we get alerted from TclOO that the object corresponding + * to an Itcl class (or its namespace...) is being torn down. + */ + + ItclClass *iclsPtr = clientData; + Tcl_Object oPtr = iclsPtr->oPtr; + Tcl_Namespace *ooNsPtr = Tcl_GetObjectNamespace(oPtr); + + if (ooNsPtr != iclsPtr->nsPtr) { + /* + * Itcl's idea of the class namespace is different from that of TclOO. + * Make sure both get torn down and pulled from tables. + */ + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&iclsPtr->infoPtr->namespaceClasses, + (char *)ooNsPtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } + Tcl_DeleteNamespace(iclsPtr->nsPtr); + } else { + ItclDestroyClass2(iclsPtr); + } + ItclReleaseClass(iclsPtr); +} + +static int +CallNewObjectInstance( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + ItclObjectInfo *infoPtr = data[0]; + const char* path = data[1]; + Tcl_Object *oPtr = data[2]; + Tcl_Obj *nameObjPtr = data[3]; + + *oPtr = Tcl_NewObjectInstance(interp, infoPtr->clazzClassPtr, + path, path, 0, NULL, 0); + if (*oPtr == NULL) { + Tcl_AppendResult(interp, + "ITCL: cannot create Tcl_NewObjectInstance for class \"", + Tcl_GetString(nameObjPtr), "\"", NULL); + return TCL_ERROR; + } + return TCL_OK; +} +/* + * ------------------------------------------------------------------------ + * Itcl_CreateClass() + * + * Creates a namespace and its associated class definition data. + * If a namespace already exists with that name, then this routine + * returns TCL_ERROR, along with an error message in the interp. + * If successful, it returns TCL_OK and a pointer to the new class + * definition. + * ------------------------------------------------------------------------ + */ +int +Itcl_CreateClass( + Tcl_Interp* interp, /* interpreter that will contain new class */ + const char* path, /* name of new class */ + ItclObjectInfo *infoPtr, /* info for all known objects */ + ItclClass **rPtr) /* returns: pointer to class definition */ +{ + const char *head; + const char *tail; + Tcl_DString buffer; + Tcl_Command cmd; + Tcl_CmdInfo cmdInfo; + Tcl_Namespace *classNs, *ooNs; + Tcl_Object oPtr; + Tcl_Obj *nameObjPtr; + Tcl_Obj *namePtr; + ItclClass *iclsPtr; + ItclVariable *ivPtr; + Tcl_HashEntry *hPtr; + void *callbackPtr; + int result; + int newEntry; + ItclResolveInfo *resolveInfoPtr; + Tcl_Obj *cmdNamePtr; + + /* + * check for an empty class name to avoid a crash + */ + if (strlen(path) == 0) { + Tcl_AppendResult(interp, "invalid class name \"\"", NULL); + return TCL_ERROR; + } + /* + * Make sure that a class with the given name does not + * already exist in the current namespace context. If a + * namespace exists, that's okay. It may have been created + * to contain stubs during a "namespace import" operation. + * We'll just replace the namespace data below with the + * proper class data. + */ + classNs = Tcl_FindNamespace(interp, (const char *)path, + (Tcl_Namespace*)NULL, /* flags */ 0); + + if (classNs != NULL && Itcl_IsClassNamespace(classNs)) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "class \"", path, "\" already exists", + (char*)NULL); + return TCL_ERROR; + } + + oPtr = NULL; + /* + * Make sure that a command with the given class name does not + * already exist in the current namespace. This prevents the + * usual Tcl commands from being clobbered when a programmer + * makes a bogus call like "class info". + */ + cmd = Tcl_FindCommand(interp, (const char *)path, + (Tcl_Namespace*)NULL, /* flags */ TCL_NAMESPACE_ONLY); + + if (cmd != NULL && !Itcl_IsStub(cmd)) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "command \"", path, "\" already exists", + (char*)NULL); + + if (strstr(path,"::") == NULL) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + " in namespace \"", + Tcl_GetCurrentNamespace(interp)->fullName, "\"", + (char*)NULL); + } + return TCL_ERROR; + } + + /* + * Make sure that the class name does not have any goofy + * characters: + * + * . => reserved for member access like: class.publicVar + */ + Itcl_ParseNamespPath(path, &buffer, &head, &tail); + + if (strstr(tail,".")) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "bad class name \"", tail, "\"", + (char*)NULL); + Tcl_DStringFree(&buffer); + return TCL_ERROR; + } + Tcl_DStringFree(&buffer); + + /* + * Allocate class definition data. + */ + iclsPtr = (ItclClass*)ckalloc(sizeof(ItclClass)); + memset(iclsPtr, 0, sizeof(ItclClass)); + iclsPtr->interp = interp; + iclsPtr->infoPtr = infoPtr; + Itcl_PreserveData((ClientData)infoPtr); + + Tcl_InitObjHashTable(&iclsPtr->variables); + Tcl_InitObjHashTable(&iclsPtr->functions); + Tcl_InitObjHashTable(&iclsPtr->options); + Tcl_InitObjHashTable(&iclsPtr->components); + Tcl_InitObjHashTable(&iclsPtr->delegatedOptions); + Tcl_InitObjHashTable(&iclsPtr->delegatedFunctions); + Tcl_InitObjHashTable(&iclsPtr->methodVariables); + Tcl_InitObjHashTable(&iclsPtr->resolveCmds); + + iclsPtr->numInstanceVars = 0; + Tcl_InitHashTable(&iclsPtr->classCommons, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&iclsPtr->resolveVars, TCL_ONE_WORD_KEYS); + Tcl_InitHashTable(&iclsPtr->contextCache, TCL_ONE_WORD_KEYS); + + Itcl_InitList(&iclsPtr->bases); + Itcl_InitList(&iclsPtr->derived); + + resolveInfoPtr = (ItclResolveInfo *) ckalloc(sizeof(ItclResolveInfo)); + memset (resolveInfoPtr, 0, sizeof(ItclResolveInfo)); + resolveInfoPtr->flags = ITCL_RESOLVE_CLASS; + resolveInfoPtr->iclsPtr = iclsPtr; + iclsPtr->resolvePtr = (Tcl_Resolve *)ckalloc(sizeof(Tcl_Resolve)); + iclsPtr->resolvePtr->cmdProcPtr = Itcl_CmdAliasProc; + iclsPtr->resolvePtr->varProcPtr = Itcl_VarAliasProc; + iclsPtr->resolvePtr->clientData = resolveInfoPtr; + iclsPtr->flags = infoPtr->currClassFlags; + + /* + * Initialize the heritage info--each class starts with its + * own class definition in the heritage. Base classes are + * added to the heritage from the "inherit" statement. + */ + Tcl_InitHashTable(&iclsPtr->heritage, TCL_ONE_WORD_KEYS); + (void) Tcl_CreateHashEntry(&iclsPtr->heritage, (char*)iclsPtr, &newEntry); + + /* + * Create a namespace to represent the class. Add the class + * definition info as client data for the namespace. If the + * namespace already exists, then replace any existing client + * data with the class data. + */ + + ItclPreserveClass(iclsPtr); + + nameObjPtr = Tcl_NewStringObj("", 0); + Tcl_IncrRefCount(nameObjPtr); + if ((path[0] != ':') || (path[1] != ':')) { + Tcl_Namespace *currNsPtr = Tcl_GetCurrentNamespace(interp); + Tcl_AppendToObj(nameObjPtr, currNsPtr->fullName, -1); + if (currNsPtr->parentPtr != NULL) { + Tcl_AppendToObj(nameObjPtr, "::", 2); + } + } + Tcl_AppendToObj(nameObjPtr, path, -1); + { + /* + * TclOO machinery will refuse to overwrite an existing command + * with creation of a new object. However, Itcl has a legacy + * "stubs" auto-importing mechanism that explicitly needs such + * overwriting. So, check whether we have a stub, and if so, + * delete it before TclOO has a chance to object. + */ + Tcl_Command oldCmd = Tcl_FindCommand(interp, path, NULL, 0); + + if (Itcl_IsStub(oldCmd)) { + Tcl_DeleteCommandFromToken(interp, oldCmd); + } + } + callbackPtr = Itcl_GetCurrentCallbackPtr(interp); + /* + * Create a command in the current namespace to manage the class: + * <className> + * <className> <objName> ?<constructor-args>? + */ + Tcl_NRAddCallback(interp, CallNewObjectInstance, infoPtr, + (ClientData)path, &oPtr, nameObjPtr); + result = Itcl_NRRunCallbacks(interp, callbackPtr); + if (result == TCL_ERROR) { + result = TCL_ERROR; + goto errorOut; + } + iclsPtr->clsPtr = Tcl_GetObjectAsClass(oPtr); + iclsPtr->oPtr = oPtr; + ItclPreserveClass(iclsPtr); + Tcl_ObjectSetMetadata(iclsPtr->oPtr, infoPtr->class_meta_type, iclsPtr); + cmd = Tcl_GetObjectCommand(iclsPtr->oPtr); + Tcl_GetCommandInfoFromToken(cmd, &cmdInfo); + cmdInfo.deleteProc = ItclDestroyClass; + cmdInfo.deleteData = iclsPtr; + Tcl_SetCommandInfoFromToken(cmd, &cmdInfo); + ooNs = Tcl_GetObjectNamespace(oPtr); + classNs = Tcl_FindNamespace(interp, Tcl_GetString(nameObjPtr), + (Tcl_Namespace*)NULL, /* flags */ 0); + if (_TclOONamespaceDeleteProc == NULL) { + _TclOONamespaceDeleteProc = ooNs->deleteProc; + } + + if (classNs == NULL) { + Tcl_AppendResult(interp, + "ITCL: cannot create/get class namespace for class \"", + Tcl_GetString(iclsPtr->fullNamePtr), "\"", NULL); + return TCL_ERROR; + } + + if (iclsPtr->infoPtr->useOldResolvers) { +#ifdef NEW_PROTO_RESOLVER + Itcl_SetNamespaceResolvers(ooNs, + (Tcl_ResolveCmdProc*)Itcl_ClassCmdResolver2, + (Tcl_ResolveVarProc*)Itcl_ClassVarResolver2, + (Tcl_ResolveCompiledVarProc*)Itcl_ClassCompiledVarResolver2); + Itcl_SetNamespaceResolvers(classNs, + (Tcl_ResolveCmdProc*)Itcl_ClassCmdResolver2, + (Tcl_ResolveVarProc*)Itcl_ClassVarResolver2, + (Tcl_ResolveCompiledVarProc*)Itcl_ClassCompiledVarResolver2); +#else + Itcl_SetNamespaceResolvers(ooNs, + (Tcl_ResolveCmdProc*)Itcl_ClassCmdResolver, + (Tcl_ResolveVarProc*)Itcl_ClassVarResolver, + (Tcl_ResolveCompiledVarProc*)Itcl_ClassCompiledVarResolver); + Itcl_SetNamespaceResolvers(classNs, + (Tcl_ResolveCmdProc*)Itcl_ClassCmdResolver, + (Tcl_ResolveVarProc*)Itcl_ClassVarResolver, + (Tcl_ResolveCompiledVarProc*)Itcl_ClassCompiledVarResolver); +#endif + } else { + Tcl_SetNamespaceResolver(ooNs, iclsPtr->resolvePtr); + Tcl_SetNamespaceResolver(classNs, iclsPtr->resolvePtr); + } + iclsPtr->nsPtr = classNs; + + + iclsPtr->namePtr = Tcl_NewStringObj(classNs->name, -1); + Tcl_IncrRefCount(iclsPtr->namePtr); + + iclsPtr->fullNamePtr = Tcl_NewStringObj(classNs->fullName, -1); + Tcl_IncrRefCount(iclsPtr->fullNamePtr); + + hPtr = Tcl_CreateHashEntry(&infoPtr->nameClasses, + (char *)iclsPtr->fullNamePtr, &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)iclsPtr); + + + hPtr = Tcl_CreateHashEntry(&infoPtr->namespaceClasses, (char *)classNs, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)iclsPtr); + if (classNs != ooNs) { + hPtr = Tcl_CreateHashEntry(&infoPtr->namespaceClasses, (char *)ooNs, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)iclsPtr); + + if (classNs->clientData && classNs->deleteProc) { + (*classNs->deleteProc)(classNs->clientData); + } + classNs->clientData = (ClientData)iclsPtr; + classNs->deleteProc = ItclDestroyClass2; +} + + hPtr = Tcl_CreateHashEntry(&infoPtr->classes, (char *)iclsPtr, &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)iclsPtr); + + /* + * now build the namespace for the common private and protected variables + * public variables go directly to the class namespace + */ + Tcl_DStringInit(&buffer); + Tcl_DStringAppend(&buffer, ITCL_VARIABLES_NAMESPACE, -1); + Tcl_DStringAppend(&buffer, + (Tcl_GetObjectNamespace(iclsPtr->oPtr))->fullName, -1); + if ((NULL == Tcl_FindNamespace(interp, Tcl_DStringValue(&buffer), NULL, + TCL_GLOBAL_ONLY)) && (NULL == Tcl_CreateNamespace(interp, + Tcl_DStringValue(&buffer), NULL, 0))) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, "ITCL: cannot create variables namespace \"", + Tcl_DStringValue(&buffer), "\"", NULL); + result = TCL_ERROR; + goto errorOut; + } + + /* + * Add the built-in "this" command to the list of function members. + */ + Tcl_DStringInit(&buffer); + Tcl_DStringAppend(&buffer, Tcl_GetString(iclsPtr->fullNamePtr), -1); + Tcl_DStringAppend(&buffer, "::this", -1); + iclsPtr->thisCmd = Tcl_CreateObjCommand(interp, Tcl_DStringValue(&buffer), + Itcl_ThisCmd, iclsPtr, NULL); + + /* + * Add the built-in "type" variable to the list of data members. + */ + if (iclsPtr->flags & ITCL_TYPE) { + namePtr = Tcl_NewStringObj("type", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_TYPE_VAR; /* mark as "type" variable */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + } + + if (iclsPtr->flags & (ITCL_ECLASS)) { + namePtr = Tcl_NewStringObj("win", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_WIN_VAR; /* mark as "win" variable */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + } + if (iclsPtr->flags & (ITCL_TYPE|ITCL_WIDGET|ITCL_WIDGETADAPTOR)) { + namePtr = Tcl_NewStringObj("self", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_SELF_VAR; /* mark as "self" variable */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + + namePtr = Tcl_NewStringObj("selfns", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_SELFNS_VAR; /* mark as "selfns" variable */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + + namePtr = Tcl_NewStringObj("win", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_WIN_VAR; /* mark as "win" variable */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + } + namePtr = Tcl_NewStringObj("this", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_THIS_VAR; /* mark as "this" variable */ + + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + + if (infoPtr->currClassFlags & + (ITCL_ECLASS|ITCL_TYPE|ITCL_WIDGETADAPTOR|ITCL_WIDGET)) { + /* + * Add the built-in "itcl_options" variable to the list of + * data members. + */ + namePtr = Tcl_NewStringObj("itcl_options", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_OPTIONS_VAR; /* mark as "itcl_options" + * variable */ + + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + + } + if (infoPtr->currClassFlags & + ITCL_ECLASS) { + /* + * Add the built-in "itcl_option_components" variable to the list of + * data members. + */ + namePtr = Tcl_NewStringObj("itcl_option_components", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_OPTION_COMP_VAR; /* mark as "itcl_option_components" + * variable */ + + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + + } + if (infoPtr->currClassFlags & (ITCL_WIDGET|ITCL_WIDGETADAPTOR)) { + /* + * Add the built-in "thiswin" variable to the list of data members. + */ + namePtr = Tcl_NewStringObj("thiswin", -1); + (void) Itcl_CreateVariable(interp, iclsPtr, namePtr, (char*)NULL, + (char*)NULL, &ivPtr); + + ivPtr->protection = ITCL_PROTECTED; /* always "protected" */ + ivPtr->flags |= ITCL_THIS_VAR; /* mark as "thiswin" variable */ + + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, + &newEntry); + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + } + if (infoPtr->currClassFlags & (ITCL_WIDGET|ITCL_WIDGETADAPTOR)) { + /* create the itcl_hull component */ + ItclComponent *icPtr; + namePtr = Tcl_NewStringObj("itcl_hull", 9); + /* itcl_hull must not be an ITCL_COMMON!! */ + if (ItclCreateComponent(interp, iclsPtr, namePtr, 0, &icPtr) != + TCL_OK) { + result = TCL_ERROR; + goto errorOut; + } + } + + ItclPreserveClass(iclsPtr); + iclsPtr->accessCmd = Tcl_GetObjectCommand(oPtr); + + cmdNamePtr = Tcl_NewObj(); + Tcl_GetCommandFullName(interp, iclsPtr->accessCmd, cmdNamePtr); + + Tcl_TraceCommand(interp, Tcl_GetString(cmdNamePtr), + TCL_TRACE_DELETE, ClassCmdDeleteTrace, iclsPtr); + + Tcl_DecrRefCount(cmdNamePtr); + /* FIXME should set the class objects unknown command to Itcl_HandleClass */ + + *rPtr = iclsPtr; + result = TCL_OK; +errorOut: + Tcl_DecrRefCount(nameObjPtr); + return result; +} + + +/* + * ------------------------------------------------------------------------ + * ItclDeleteClassVariablesNamespace() + * + * ------------------------------------------------------------------------ + */ +void +ItclDeleteClassVariablesNamespace( + Tcl_Interp *interp, + ItclClass *iclsPtr) +{ + /* TODO: why is this being skipped? */ + return; +} + +static int +CallDeleteOneObject( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch place; + ItclClass *iclsPtr2 = NULL; + ItclObject *contextIoPtr; + ItclClass *iclsPtr = data[0]; + ItclObjectInfo *infoPtr = data[1]; + void *callbackPtr; + int classIsDeleted; + + if (result != TCL_OK) { + return result; + } + classIsDeleted = 0; + hPtr = Tcl_FindHashEntry(&infoPtr->classes, (char *)iclsPtr); + if (hPtr == NULL) { + classIsDeleted = 1; + } + if (classIsDeleted) { + return result; + } + /* + * Fix 227804: Whenever an object to delete was found we + * have to reset the search to the beginning as the + * current entry in the search was deleted and accessing it + * is therefore not allowed anymore. + */ + + hPtr = Tcl_FirstHashEntry(&infoPtr->objects, &place); + if (hPtr) { + contextIoPtr = (ItclObject*)Tcl_GetHashValue(hPtr); + + while (contextIoPtr->iclsPtr != iclsPtr) { + hPtr = Tcl_NextHashEntry(&place); + if (hPtr == NULL) { + break; + } + } + if (hPtr) { + callbackPtr = Itcl_GetCurrentCallbackPtr(interp); + if (Itcl_DeleteObject(interp, contextIoPtr) != TCL_OK) { + iclsPtr2 = iclsPtr; + goto deleteClassFail; + } + + Tcl_NRAddCallback(interp, CallDeleteOneObject, iclsPtr, + infoPtr, NULL, NULL); + return Itcl_NRRunCallbacks(interp, callbackPtr); + } + + } + + return TCL_OK; + +deleteClassFail: + /* check if class is not yet deleted */ + hPtr = Tcl_FindHashEntry(&infoPtr->classes, (char *)iclsPtr2); + if (hPtr != NULL) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (while deleting class \"%s\")", + iclsPtr2->nsPtr->fullName)); + } + return TCL_ERROR; +} + +static int +CallDeleteOneClass( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_HashEntry *hPtr; + ItclClass *iclsPtr = data[0]; + ItclObjectInfo *infoPtr = data[1]; + int isDerivedReleased; + + if (result != TCL_OK) { + return result; + } + isDerivedReleased = iclsPtr->flags & ITCL_CLASS_DERIVED_RELEASED; + result = Itcl_DeleteClass(interp, iclsPtr); + if (!isDerivedReleased) { + if (result == TCL_OK) { + hPtr = Tcl_FindHashEntry(&infoPtr->classes, (char *)iclsPtr); + if (hPtr != NULL) { + /* release from derived reference */ + ItclReleaseClass(iclsPtr); + } + } + } + if (result == TCL_OK) { + return TCL_OK; + } + + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (while deleting class \"%s\")", + iclsPtr->nsPtr->fullName)); + return TCL_ERROR; +} +/* + * ------------------------------------------------------------------------ + * Itcl_DeleteClass() + * + * Deletes a class by deleting all derived classes and all objects in + * that class, and finally, by destroying the class namespace. This + * procedure provides a friendly way of doing this. If any errors + * are detected along the way, the process is aborted. + * + * Returns TCL_OK if successful, or TCL_ERROR (along with an error + * message in the interpreter) if anything goes wrong. + * ------------------------------------------------------------------------ + */ +int +Itcl_DeleteClass( + Tcl_Interp *interp, /* interpreter managing this class */ + ItclClass *iclsPtr) /* class */ +{ + Tcl_HashEntry *hPtr; + ItclObjectInfo *infoPtr; + ItclClass *iclsPtr2 = NULL; + Itcl_ListElem *elem; + void *callbackPtr; + int result; + + infoPtr = Tcl_GetAssocData(interp, ITCL_INTERP_DATA, NULL); + hPtr = Tcl_FindHashEntry(&infoPtr->classes, (char *)iclsPtr); + if (hPtr == NULL) { + /* class has already been deleted */ + return TCL_OK; + } + if (iclsPtr->flags & ITCL_CLASS_IS_DELETED) { + return TCL_OK; + } + iclsPtr->flags |= ITCL_CLASS_IS_DELETED; + /* + * Destroy all derived classes, since these lose their meaning + * when the base class goes away. If anything goes wrong, + * abort with an error. + * + * TRICKY NOTE: When a derived class is destroyed, it + * automatically deletes itself from the "derived" list. + */ + elem = Itcl_FirstListElem(&iclsPtr->derived); + while (elem) { + iclsPtr2 = (ItclClass*)Itcl_GetListValue(elem); + elem = Itcl_NextListElem(elem); /* advance here--elem will go away */ + + callbackPtr = Itcl_GetCurrentCallbackPtr(interp); + Tcl_NRAddCallback(interp, CallDeleteOneClass, iclsPtr2, + iclsPtr2->infoPtr, NULL, NULL); + result = Itcl_NRRunCallbacks(interp, callbackPtr); + if (result != TCL_OK) { + return result; + } + } + + /* + * Scan through and find all objects that belong to this class. + * Note that more specialized objects have already been + * destroyed above, when derived classes were destroyed. + * Destroy objects and report any errors. + */ + /* + * we have to enroll the while loop to fit for NRE + * so we add a callback to delete the first element + * and run this callback. At the end of the execution of that callback + * we add eventually a callback for the next element and run that etc ... + * if an error occurs we terminate the enrolled loop and return + * otherwise we return at the end of the enrolled loop. + */ + callbackPtr = Itcl_GetCurrentCallbackPtr(interp); + Tcl_NRAddCallback(interp, CallDeleteOneObject, iclsPtr, + iclsPtr->infoPtr, NULL, NULL); + result = Itcl_NRRunCallbacks(interp, callbackPtr); + if (result != TCL_OK) { + return result; + } + /* + * Destroy the namespace associated with this class. + * + * TRICKY NOTE: + * The cleanup procedure associated with the namespace is + * invoked automatically. It does all of the same things + * above, but it also disconnects this class from its + * base-class lists, and removes the class access command. + */ + ItclDeleteClassVariablesNamespace(interp, iclsPtr); + Tcl_DeleteNamespace(iclsPtr->nsPtr); + return TCL_OK; +} + + +/* + * ------------------------------------------------------------------------ + * ItclDestroyClass() + * + * Invoked whenever the access command for a class is destroyed. + * Destroys the namespace associated with the class, which also + * destroys all objects in the class and all derived classes. + * Disconnects this class from the "derived" class lists of its + * base classes, and releases any claim to the class definition + * data. If this is the last use of that data, the class will + * completely vanish at this point. + * ------------------------------------------------------------------------ + */ +static void +ItclDestroyClass( + ClientData cdata) /* class definition to be destroyed */ +{ + ItclClass *iclsPtr = (ItclClass*)cdata; + + if (iclsPtr->flags & ITCL_CLASS_IS_DESTROYED) { + return; + } + iclsPtr->flags |= ITCL_CLASS_IS_DESTROYED; + if (!(iclsPtr->flags & ITCL_CLASS_NS_IS_DESTROYED)) { + if (iclsPtr->accessCmd) { + Tcl_DeleteCommandFromToken(iclsPtr->interp, iclsPtr->accessCmd); + iclsPtr->accessCmd = NULL; + } + Tcl_DeleteNamespace(iclsPtr->nsPtr); + } + ItclReleaseClass(iclsPtr); +} + + +/* + * ------------------------------------------------------------------------ + * ItclDestroyClassNamesp() + * + * Invoked whenever the namespace associated with a class is destroyed. + * Destroys all objects associated with this class and all derived + * classes. Disconnects this class from the "derived" class lists + * of its base classes, and removes the class access command. Releases + * any claim to the class definition data. If this is the last use + * of that data, the class will completely vanish at this point. + * ------------------------------------------------------------------------ + */ +void +ItclDestroyClassNamesp( + ClientData cdata) /* class definition to be destroyed */ +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch place; + Tcl_Command cmdPtr; + ItclClass *iclsPtr; + ItclObject *ioPtr; + Itcl_ListElem *elem; + Itcl_ListElem *belem; + ItclClass *iclsPtr2; + ItclClass *basePtr; + ItclClass *derivedPtr; + + + iclsPtr = (ItclClass*)cdata; + if (iclsPtr->flags & ITCL_CLASS_NS_IS_DESTROYED) { + return; + } + iclsPtr->flags |= ITCL_CLASS_NS_IS_DESTROYED; + /* + * Destroy all derived classes, since these lose their meaning + * when the base class goes away. + * + * TRICKY NOTE: When a derived class is destroyed, it + * automatically deletes itself from the "derived" list. + */ + elem = Itcl_FirstListElem(&iclsPtr->derived); + while (elem) { + iclsPtr2 = (ItclClass*)Itcl_GetListValue(elem); + if (iclsPtr2->nsPtr != NULL) { + Tcl_DeleteNamespace(iclsPtr2->nsPtr); + } + + /* As the first namespace is now destroyed we have to get the + * new first element of the hash table. We cannot go to the + * next element from the current one, because the current one + * is deleted. itcl Patch #593112, for Bug #577719. + */ + + elem = Itcl_FirstListElem(&iclsPtr->derived); + } + + /* + * Scan through and find all objects that belong to this class. + * Destroy them quietly by deleting their access command. + */ + hPtr = Tcl_FirstHashEntry(&iclsPtr->infoPtr->objects, &place); + while (hPtr) { + ioPtr = (ItclObject*)Tcl_GetHashValue(hPtr); + if (ioPtr->iclsPtr == iclsPtr) { + if ((ioPtr->accessCmd != NULL) && (!(ioPtr->flags & + (ITCL_OBJECT_IS_DESTRUCTED)))) { + ItclPreserveObject(ioPtr); + Tcl_DeleteCommandFromToken(iclsPtr->interp, ioPtr->accessCmd); + ioPtr->accessCmd = NULL; + ItclReleaseObject(ioPtr); + /* + * Fix 227804: Whenever an object to delete was found we + * have to reset the search to the beginning as the + * current entry in the search was deleted and accessing it + * is therefore not allowed anymore. + */ + + hPtr = Tcl_FirstHashEntry(&iclsPtr->infoPtr->objects, &place); + continue; + } + } + hPtr = Tcl_NextHashEntry(&place); + } + + /* + * Next, remove this class from the "derived" list in + * all base classes. + */ + belem = Itcl_FirstListElem(&iclsPtr->bases); + while (belem) { + basePtr = (ItclClass*)Itcl_GetListValue(belem); + + elem = Itcl_FirstListElem(&basePtr->derived); + while (elem) { + derivedPtr = (ItclClass*)Itcl_GetListValue(elem); + if (derivedPtr == iclsPtr) { + derivedPtr->flags |= ITCL_CLASS_DERIVED_RELEASED; + ItclReleaseClass(derivedPtr); + elem = Itcl_DeleteListElem(elem); + } else { + elem = Itcl_NextListElem(elem); + } + } + belem = Itcl_NextListElem(belem); + } + + /* + * Next, destroy the access command associated with the class. + */ + iclsPtr->flags |= ITCL_CLASS_NS_TEARDOWN; + if (iclsPtr->accessCmd) { + cmdPtr = iclsPtr->accessCmd; + iclsPtr->accessCmd = NULL; + Tcl_DeleteCommandFromToken(iclsPtr->interp, cmdPtr); + } + + /* + * Release the namespace's claim on the class definition. + */ + ItclReleaseClass(iclsPtr); +} + + +/* + * ------------------------------------------------------------------------ + * ItclFreeClass() + * + * Frees all memory associated with a class definition. This is + * usually invoked automatically by Itcl_ReleaseData(), when class + * data is no longer being used. + * ------------------------------------------------------------------------ + */ +static void +ItclFreeClass( + char *cdata) /* class definition to be destroyed */ +{ + FOREACH_HASH_DECLS; + Tcl_HashSearch place; + ItclClass *iclsPtr; + ItclMemberFunc *imPtr; + ItclVariable *ivPtr; + ItclOption *ioptPtr; + ItclComponent *icPtr; + ItclDelegatedOption *idoPtr; + ItclDelegatedFunction *idmPtr; + Itcl_ListElem *elem; + ItclVarLookup *vlookup; + ItclCmdLookup *clookupPtr; + Tcl_Var var; + + iclsPtr = (ItclClass*)cdata; + if (iclsPtr->flags & ITCL_CLASS_IS_FREED) { + return; + } + ItclDeleteClassesDictInfo(iclsPtr->interp, iclsPtr); + iclsPtr->flags |= ITCL_CLASS_IS_FREED; + + /* + * Tear down the list of derived classes. This list should + * really be empty if everything is working properly, but + * release it here just in case. + */ + elem = Itcl_FirstListElem(&iclsPtr->derived); + while (elem) { + ItclReleaseClass( Itcl_GetListValue(elem) ); + elem = Itcl_NextListElem(elem); + } + Itcl_DeleteList(&iclsPtr->derived); + + /* + * Tear down the variable resolution table. Some records + * appear multiple times in the table (for x, foo::x, etc.) + * so each one has a reference count. + */ +/* Tcl_InitHashTable(&varTable, TCL_STRING_KEYS); */ + + FOREACH_HASH_VALUE(vlookup, &iclsPtr->resolveVars) { + if (--vlookup->usage == 0) { + /* + * If this is a common variable owned by this class, + * then release the class's hold on it. FIXME !!! + */ + ckfree((char*)vlookup); + } + } + + Tcl_DeleteHashTable(&iclsPtr->resolveVars); + + /* + * Tear down the virtual method table... + */ + while (1) { + hPtr = Tcl_FirstHashEntry(&iclsPtr->resolveCmds, &place); + if (hPtr == NULL) { + break; + } + clookupPtr = Tcl_GetHashValue(hPtr); + ckfree((char *)clookupPtr); + Tcl_DeleteHashEntry(hPtr); + } + Tcl_DeleteHashTable(&iclsPtr->resolveCmds); + + /* + * Delete all option definitions. + */ + while (1) { + hPtr = Tcl_FirstHashEntry(&iclsPtr->options, &place); + if (hPtr == NULL) { + break; + } + ioptPtr = Tcl_GetHashValue(hPtr); + Tcl_DeleteHashEntry(hPtr); + Itcl_ReleaseData(ioptPtr); + } + Tcl_DeleteHashTable(&iclsPtr->options); + + /* + * Delete all function definitions. + */ + FOREACH_HASH_VALUE(imPtr, &iclsPtr->functions) { + imPtr->iclsPtr = NULL; + ItclReleaseIMF(imPtr); + } + Tcl_DeleteHashTable(&iclsPtr->functions); + + /* + * Delete all delegated options. + */ + FOREACH_HASH_VALUE(idoPtr, &iclsPtr->delegatedOptions) { + Itcl_ReleaseData(idoPtr); + } + Tcl_DeleteHashTable(&iclsPtr->delegatedOptions); + + /* + * Delete all delegated functions. + */ + FOREACH_HASH_VALUE(idmPtr, &iclsPtr->delegatedFunctions) { + if (idmPtr->icPtr != NULL) { + if (idmPtr->icPtr->ivPtr->iclsPtr == iclsPtr) { + ItclDeleteDelegatedFunction(idmPtr); + } + } + } + Tcl_DeleteHashTable(&iclsPtr->delegatedFunctions); + + /* + * Delete all components + */ + while (1) { + hPtr = Tcl_FirstHashEntry(&iclsPtr->components, &place); + if (hPtr == NULL) { + break; + } + icPtr = Tcl_GetHashValue(hPtr); + Tcl_DeleteHashEntry(hPtr); + if (icPtr != NULL) { + ItclDeleteComponent(icPtr); + } + } + Tcl_DeleteHashTable(&iclsPtr->components); + + /* + * Delete all variable definitions. + */ + while (1) { + hPtr = Tcl_FirstHashEntry(&iclsPtr->variables, &place); + if (hPtr == NULL) { + break; + } + ivPtr = Tcl_GetHashValue(hPtr); + Tcl_DeleteHashEntry(hPtr); + if (ivPtr != NULL) { + Itcl_ReleaseData(ivPtr); + } + } + Tcl_DeleteHashTable(&iclsPtr->variables); + + /* + * Release the claim on all base classes. + */ + elem = Itcl_FirstListElem(&iclsPtr->bases); + while (elem) { + ItclReleaseClass( Itcl_GetListValue(elem) ); + elem = Itcl_NextListElem(elem); + } + Itcl_DeleteList(&iclsPtr->bases); + Tcl_DeleteHashTable(&iclsPtr->heritage); + + /* remove owerself from the all classes entry */ + hPtr = Tcl_FindHashEntry(&iclsPtr->infoPtr->nameClasses, + (char *)iclsPtr->fullNamePtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } + + /* remove owerself from the all namespaceClasses entry */ + hPtr = Tcl_FindHashEntry(&iclsPtr->infoPtr->namespaceClasses, + (char *)iclsPtr->nsPtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } + + /* remove owerself from the all classes entry */ + hPtr = Tcl_FindHashEntry(&iclsPtr->infoPtr->classes, (char *)iclsPtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } + + /* FIXME !!! + free contextCache + free resolvePtr -- this is only needed for CallFrame Resolvers + -- not used at the moment + */ + + FOREACH_HASH_VALUE(var, &iclsPtr->classCommons) { + Itcl_ReleaseVar(var); + } + Tcl_DeleteHashTable(&iclsPtr->classCommons); + + /* + * Free up the widget class name + */ + if (iclsPtr->widgetClassPtr != NULL) { + Tcl_DecrRefCount(iclsPtr->widgetClassPtr); + } + + /* + * Free up the widget hulltype name + */ + if (iclsPtr->hullTypePtr != NULL) { + Tcl_DecrRefCount(iclsPtr->hullTypePtr); + } + + /* + * Free up the type typeconstrutor code + */ + + if (iclsPtr->typeConstructorPtr != NULL) { + Tcl_DecrRefCount(iclsPtr->typeConstructorPtr); + } + + /* + * Free up the object initialization code. + */ + if (iclsPtr->initCode) { + Tcl_DecrRefCount(iclsPtr->initCode); + } + + Itcl_ReleaseData((ClientData)iclsPtr->infoPtr); + + Tcl_DecrRefCount(iclsPtr->namePtr); + Tcl_DecrRefCount(iclsPtr->fullNamePtr); + + if (iclsPtr->resolvePtr != NULL) { + ckfree((char *)iclsPtr->resolvePtr->clientData); + ckfree((char *)iclsPtr->resolvePtr); + } + ckfree((char*)iclsPtr); +} + + +/* + * ------------------------------------------------------------------------ + * Itcl_IsClassNamespace() + * + * Checks to see whether or not the given namespace represents an + * [incr Tcl] class. Returns non-zero if so, and zero otherwise. + * ------------------------------------------------------------------------ + */ +int +Itcl_IsClassNamespace( + Tcl_Namespace *nsPtr) /* namespace being tested */ +{ + ItclClass *iclsPtr = ItclNamespace2Class(nsPtr); + return iclsPtr != NULL; +} + + +/* + * ------------------------------------------------------------------------ + * Itcl_IsClass() + * + * Checks the given Tcl command to see if it represents an itcl class. + * Returns non-zero if the command is associated with a class. + * ------------------------------------------------------------------------ + */ +int +Itcl_IsClass( + Tcl_Command cmd) /* command being tested */ +{ + Tcl_CmdInfo cmdInfo; + + if (Tcl_GetCommandInfoFromToken(cmd, &cmdInfo) == 0) { + return 0; + } + if (cmdInfo.deleteProc == ItclDestroyClass) { + return 1; + } + + /* + * This may be an imported command. Try to get the real + * command and see if it represents a class. + */ + cmd = Tcl_GetOriginalCommand(cmd); + if (cmd != NULL) { + if (Tcl_GetCommandInfoFromToken(cmd, &cmdInfo) == 0) { + return 0; + } + if (cmdInfo.deleteProc == ItclDestroyClass) { + return 1; + } + } + return 0; +} + + +/* + * ------------------------------------------------------------------------ + * Itcl_FindClass() + * + * Searches for the specified class in the active namespace. If the + * class is found, this procedure returns a pointer to the class + * definition. Otherwise, if the autoload flag is non-zero, an + * attempt will be made to autoload the class definition. If it + * still can't be found, this procedure returns NULL, along with an + * error message in the interpreter. + * ------------------------------------------------------------------------ + */ +ItclClass* +Itcl_FindClass( + Tcl_Interp* interp, /* interpreter containing class */ + const char* path, /* path name for class */ + int autoload) +{ + /* + * Search for a namespace with the specified name, and if + * one is found, see if it is a class namespace. + */ + + Tcl_Namespace* classNs = Itcl_FindClassNamespace(interp, path); + + if (classNs) { + ItclObjectInfo *infoPtr + = Tcl_GetAssocData(interp, ITCL_INTERP_DATA, NULL); + Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&infoPtr->namespaceClasses, + (char *) classNs); + if (hPtr) { + return (ItclClass *) Tcl_GetHashValue(hPtr); + } + } + + /* + * If the autoload flag is set, try to autoload the class + * definition, then search again. + */ + if (autoload) { + Tcl_DString buf; + + Tcl_DStringInit(&buf); + Tcl_DStringAppend(&buf, "::auto_load ", -1); + Tcl_DStringAppend(&buf, path, -1); + if (Tcl_EvalEx(interp, Tcl_DStringValue(&buf), -1, 0) != TCL_OK) { + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (while attempting to autoload class \"%s\")", + path)); + Tcl_DStringFree(&buf); + return NULL; + } + Tcl_ResetResult(interp); + Tcl_DStringFree(&buf); + + return Itcl_FindClass(interp, path, 0); + } + + Tcl_AppendResult(interp, "class \"", path, "\" not found in context \"", + Tcl_GetCurrentNamespace(interp)->fullName, "\"", + (char*)NULL); + + return NULL; +} + +/* + * ------------------------------------------------------------------------ + * Itcl_FindClassNamespace() + * + * Searches for the specified class namespace. The normal Tcl procedure + * Tcl_FindNamespace also searches for namespaces, but only in the + * current namespace context. This makes it hard to find one class + * from within another. For example, suppose. you have two namespaces + * Foo and Bar. If you're in the context of Foo and you look for + * Bar, you won't find it with Tcl_FindNamespace. This behavior is + * okay for namespaces, but wrong for classes. + * + * This procedure search for a class namespace. If the name is + * absolute (i.e., starts with "::"), then that one name is checked, + * and the class is either found or not. But if the name is relative, + * it is sought in the current namespace context and in the global + * context, just like the normal command lookup. + * + * This procedure returns a pointer to the desired namespace, or + * NULL if the namespace was not found. + * ------------------------------------------------------------------------ + */ +Tcl_Namespace* +Itcl_FindClassNamespace(interp, path) + Tcl_Interp* interp; /* interpreter containing class */ + const char* path; /* path name for class */ +{ + Tcl_Namespace* contextNs = Tcl_GetCurrentNamespace(interp); + Tcl_Namespace *classNs = Tcl_FindNamespace(interp, path, NULL, 0); + + if ( !classNs /* We didn't find it... */ + && contextNs->parentPtr != NULL /* context is not global */ + && (*path != ':' || *(path+1) != ':') /* path not FQ */ + ) { + + if (strcmp(contextNs->name, path) == 0) { + classNs = contextNs; + } else { + classNs = Tcl_FindNamespace(interp, path, NULL, TCL_GLOBAL_ONLY); + } + } + return classNs; +} + + +static int +FinalizeCreateObject( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_Obj *objNamePtr = data[0]; + ItclClass *iclsPtr = data[1]; + if (result == TCL_OK) { + if (!(iclsPtr->flags & (ITCL_TYPE|ITCL_WIDGET|ITCL_WIDGETADAPTOR))) { + Tcl_ResetResult(interp); + Tcl_AppendResult(interp, Tcl_GetString(objNamePtr), NULL); + } + } + Tcl_DecrRefCount(objNamePtr); + return result; +} + +static int +CallCreateObject( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_Obj *objNamePtr = data[0]; + ItclClass *iclsPtr = data[1]; + int objc = PTR2INT(data[2]); + Tcl_Obj **objv = data[3]; + + if (result == TCL_OK) { + result = ItclCreateObject(interp, Tcl_GetString(objNamePtr), iclsPtr, + objc, objv); + } + return result; +} +/* + * ------------------------------------------------------------------------ + * Itcl_HandleClass() + * + * first argument is ::itcl::parser::handleClass + * Invoked by Tcl whenever the user issues the command associated with + * a class name. Handles the following syntax: + * + * <className> + * <className> <objName> ?<args>...? + * + * Without any arguments, the command does nothing. In the olden days, + * this allowed the class name to be invoked by itself to prompt the + * autoloader to load the class definition. Today, this behavior is + * retained for backward compatibility with old releases. + * + * If arguments are specified, then this procedure creates a new + * object named <objName> in the appropriate class. Note that if + * <objName> contains "#auto", that part is automatically replaced + * by a unique string built from the class name. + * ------------------------------------------------------------------------ + */ +int +Itcl_HandleClass( + ClientData clientData, /* class definition */ + Tcl_Interp *interp, /* current interpreter */ + int objc, /* number of arguments */ + Tcl_Obj *const objv[]) /* argument objects */ +{ + if (objc > 3) { + const char *token = Tcl_GetString(objv[3]); + const char *nsEnd = NULL; + const char *pos = token; + const char *tail = pos; + int fq = 0; + int code = TCL_OK; + Tcl_Obj *nsObj, *fqObj; + + while ((pos = strstr(pos, "::"))) { + if (pos == token) { + fq = 1; + nsEnd = token; + } else if (pos[-1] != ':') { + nsEnd = pos - 1; + } + tail = pos + 2; pos++; + } + + if (fq) { + nsObj = Tcl_NewStringObj(token, nsEnd-token); + } else { + Tcl_Namespace *nsPtr = Tcl_GetCurrentNamespace(interp); + + nsObj = Tcl_NewStringObj(nsPtr->fullName, -1); + if (nsEnd) { + Tcl_AppendToObj(nsObj, "::", 2); + Tcl_AppendToObj(nsObj, token, nsEnd-token); + } + } + + fqObj = Tcl_DuplicateObj(nsObj); + Tcl_AppendToObj(fqObj, "::", 2); + Tcl_AppendToObj(fqObj, tail, -1); + + if (Tcl_GetCommandFromObj(interp, fqObj)) { + Tcl_AppendResult(interp, "command \"", tail, + "\" already exists in namespace \"", Tcl_GetString(nsObj), + "\"", NULL); + code = TCL_ERROR; + } + Tcl_DecrRefCount(fqObj); + Tcl_DecrRefCount(nsObj); + if (code != TCL_OK) { + return code; + } + } + return ItclClassCreateObject(clientData, interp, objc, objv); +} + +int +ItclClassCreateObject( + ClientData clientData, /* IclObjectInfo */ + Tcl_Interp *interp, /* current interpreter */ + int objc, /* number of arguments */ + Tcl_Obj *const objv[]) /* argument objects */ +{ + Tcl_DString buffer; /* buffer used to build object names */ + Tcl_Obj *objNamePtr; + Tcl_HashEntry *hPtr; + Tcl_Obj **newObjv; + ItclClass *iclsPtr; + ItclObjectInfo *infoPtr; + void *callbackPtr; + char unique[256]; /* buffer used for unique part of object names */ + char *token; + char *objName; + char tmp; + char *start; + char *pos; + const char *match; + + infoPtr = (ItclObjectInfo *)clientData; + Tcl_ResetResult(interp); + ItclShowArgs(1, "ItclClassCreateObject", objc, objv); + /* + * If the command is invoked without an object name, then do nothing. + * This used to support autoloading--that the class name could be + * invoked as a command by itself, prompting the autoloader to + * load the class definition. We retain the behavior here for + * backward-compatibility with earlier releases. + */ + if (objc <= 3) { + return TCL_OK; + } + + hPtr = Tcl_FindHashEntry(&infoPtr->nameClasses, (char *)objv[2]); + if (hPtr == NULL) { + Tcl_AppendResult(interp, "no such class: \"", + Tcl_GetString(objv[1]), "\"", NULL); + return TCL_ERROR; + } + iclsPtr = Tcl_GetHashValue(hPtr); + + /* + * If the object name is "::", and if this is an old-style class + * definition, then treat the remaining arguments as a command + * in the class namespace. This used to be the way of invoking + * a class proc, but the new syntax is "class::proc" (without + * spaces). + */ + token = Tcl_GetString(objv[3]); + if ((*token == ':') && (strcmp(token,"::") == 0) && (objc > 4)) { + /* + * If this is not an old-style class, then return an error + * describing the syntax change. + */ + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "syntax \"class :: proc\" is an anachronism\n", + "[incr Tcl] no longer supports this syntax.\n", + "Instead, remove the spaces from your procedure invocations:\n", + " ", + Tcl_GetString(objv[1]), "::", + Tcl_GetString(objv[4]), " ?args?", + (char*)NULL); + return TCL_ERROR; + } + + /* + * Otherwise, we have a proper object name. Create a new instance + * with that name. If the name contains "#auto", replace this with + * a uniquely generated string based on the class name. + */ + Tcl_DStringInit(&buffer); + objName = NULL; + + match = "#auto"; + start = token; + for (pos=start; *pos != '\0'; pos++) { + if (*pos == *match) { + if (*(++match) == '\0') { + tmp = *start; + *start = '\0'; /* null-terminate first part */ + + /* + * Substitute a unique part in for "#auto", and keep + * incrementing a counter until a valid name is found. + */ + do { + Tcl_CmdInfo dummy; + + sprintf(unique,"%.200s%d", Tcl_GetString(iclsPtr->namePtr), + iclsPtr->unique++); + unique[0] = tolower(UCHAR(unique[0])); + + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, token, -1); + Tcl_DStringAppend(&buffer, unique, -1); + Tcl_DStringAppend(&buffer, start+5, -1); + + objName = Tcl_DStringValue(&buffer); + + /* + * [Fix 227811] Check for any command with the + * given name, not only objects. + */ + + if (Tcl_GetCommandInfo (interp, objName, &dummy) == 0) { + break; /* if an error is found, bail out! */ + } + } while (1); + + *start = tmp; /* undo null-termination */ + objName = Tcl_DStringValue(&buffer); + break; /* object name is ready to go! */ + } + } + else { + match = "#auto"; + pos = start++; + } + } + + /* + * If "#auto" was not found, then just use object name as-is. + */ + if (objName == NULL) { + objName = token; + } + + /* + * Try to create a new object. If successful, return the + * object name as the result of this command. + */ + objNamePtr = Tcl_NewStringObj(objName, -1); + Tcl_IncrRefCount(objNamePtr); + Tcl_DStringFree(&buffer); + callbackPtr = Itcl_GetCurrentCallbackPtr(interp); + newObjv = (Tcl_Obj **)(objv+4); + Tcl_NRAddCallback(interp, FinalizeCreateObject, objNamePtr, iclsPtr, + NULL, NULL); + Tcl_NRAddCallback(interp, CallCreateObject, objNamePtr, iclsPtr, + INT2PTR(objc-4), newObjv); + return Itcl_NRRunCallbacks(interp, callbackPtr); +} + + +/* + * ------------------------------------------------------------------------ + * Itcl_BuildVirtualTables() + * + * Invoked whenever the class heritage changes or members are added or + * removed from a class definition to rebuild the member lookup + * tables. There are two tables: + * + * METHODS: resolveCmds + * Used primarily in Itcl_ClassCmdResolver() to resolve all + * command references in a namespace. + * + * DATA MEMBERS: resolveVars + * Used primarily in Itcl_ClassVarResolver() to quickly resolve + * variable references in each class scope. + * + * These tables store every possible name for each command/variable + * (member, class::member, namesp::class::member, etc.). Members + * in a derived class may shadow members with the same name in a + * base class. In that case, the simple name in the resolution + * table will point to the most-specific member. + * ------------------------------------------------------------------------ + */ +void +Itcl_BuildVirtualTables( + ItclClass* iclsPtr) /* class definition being updated */ +{ + Tcl_HashEntry *hPtr; + Tcl_HashSearch place; + Tcl_Namespace* nsPtr; + Tcl_DString buffer, buffer2; + Tcl_Obj *objPtr; + ItclVarLookup *vlookup; + ItclVariable *ivPtr; + ItclMemberFunc *imPtr; + ItclDelegatedFunction *idmPtr; + ItclHierIter hier; + ItclClass *iclsPtr2; + ItclCmdLookup *clookupPtr; +#ifdef NEW_PROTO_RESOLVER + ItclClassVarInfo *icviPtr; + ItclClassCmdInfo *icciPtr; +#endif + int newEntry; + + Tcl_DStringInit(&buffer); + Tcl_DStringInit(&buffer2); + + /* + * Clear the variable resolution table. + */ + hPtr = Tcl_FirstHashEntry(&iclsPtr->resolveVars, &place); + while (hPtr) { + vlookup = (ItclVarLookup*)Tcl_GetHashValue(hPtr); + if (--vlookup->usage == 0) { + ckfree((char*)vlookup); + } + hPtr = Tcl_NextHashEntry(&place); + } + Tcl_DeleteHashTable(&iclsPtr->resolveVars); + Tcl_InitHashTable(&iclsPtr->resolveVars, TCL_STRING_KEYS); + iclsPtr->numInstanceVars = 0; + + /* + * Set aside the first object-specific slot for the built-in + * "this" variable. Only allocate one of these, even though + * there is a definition for "this" in each class scope. + * Set aside the second and third object-specific slot for the built-in + * "itcl_options" and "itcl_option_components" variable. + */ + iclsPtr->numInstanceVars++; + iclsPtr->numInstanceVars++; + iclsPtr->numInstanceVars++; + + /* + * Scan through all classes in the hierarchy, from most to + * least specific. Add a lookup entry for each variable + * into the table. + */ + Itcl_InitHierIter(&hier, iclsPtr); + iclsPtr2 = Itcl_AdvanceHierIter(&hier); + while (iclsPtr2 != NULL) { + hPtr = Tcl_FirstHashEntry(&iclsPtr2->variables, &place); + while (hPtr) { +#ifdef NEW_PROTO_RESOLVER + int type = VAR_TYPE_VARIABLE; +#endif + ivPtr = (ItclVariable*)Tcl_GetHashValue(hPtr); + + vlookup = (ItclVarLookup *)ckalloc(sizeof(ItclVarLookup)); + vlookup->ivPtr = ivPtr; + vlookup->usage = 0; + vlookup->leastQualName = NULL; + + /* + * If this variable is PRIVATE to another class scope, + * then mark it as "inaccessible". + */ + vlookup->accessible = (ivPtr->protection != ITCL_PRIVATE || + ivPtr->iclsPtr == iclsPtr); + + if (ivPtr->flags & ITCL_COMMON) { +#ifdef NEW_PROTO_RESOLVER + type = VAR_TYPE_COMMON; +#endif + } + /* + * If this is a reference to the built-in "this" + * variable, then its index is "0". Otherwise, + * add another slot to the end of the table. + */ + if ((ivPtr->flags & ITCL_THIS_VAR) != 0) { + vlookup->varNum = 0; + } else { + if ((ivPtr->flags & ITCL_OPTIONS_VAR) != 0) { + vlookup->varNum = 1; + } else { + vlookup->varNum = iclsPtr->numInstanceVars++; + } + } +#ifdef NEW_PROTO_RESOLVER + icviPtr = (ItclClassVarInfo *)ckalloc( + sizeof(ItclClassVarInfo)); + icviPtr->type = type; + icviPtr->protection = ivPtr->protection; + icviPtr->nsPtr = iclsPtr->nsPtr; + icviPtr->declaringNsPtr = iclsPtr2->nsPtr; + icviPtr->varNum = vlookup->varNum; + ClientData clientData2; + clientData2 = Itcl_RegisterClassVariable( + iclsPtr->infoPtr->interp, iclsPtr2->nsPtr, + Tcl_GetString(ivPtr->namePtr), icviPtr); + vlookup->classVarInfoPtr = clientData2; +#endif +/* FIXME !!! should use for var lookup !! */ + + /* + * Create all possible names for this variable and enter + * them into the variable resolution table: + * var + * class::var + * namesp1::class::var + * namesp2::namesp1::class::var + * ... + */ + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, Tcl_GetString(ivPtr->namePtr), -1); + nsPtr = iclsPtr2->nsPtr; + + while (1) { + hPtr = Tcl_CreateHashEntry(&iclsPtr->resolveVars, + Tcl_DStringValue(&buffer), &newEntry); + + if (newEntry) { + Tcl_SetHashValue(hPtr, (ClientData)vlookup); + vlookup->usage++; + + if (!vlookup->leastQualName) { + vlookup->leastQualName = + Tcl_GetHashKey(&iclsPtr->resolveVars, hPtr); + } +#ifdef NEW_PROTO_RESOLVER + Itcl_RegisterClassVariable(iclsPtr->infoPtr->interp, + iclsPtr->nsPtr, Tcl_DStringValue(&buffer), + vlookup->classVarInfoPtr); +#endif + } + + if (nsPtr == NULL) { + break; + } + Tcl_DStringSetLength(&buffer2, 0); + Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&buffer), -1); + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, nsPtr->name, -1); + Tcl_DStringAppend(&buffer, "::", -1); + Tcl_DStringAppend(&buffer, Tcl_DStringValue(&buffer2), -1); + + nsPtr = nsPtr->parentPtr; + } + + /* + * If this record is not needed, free it now. + */ + if (vlookup->usage == 0) { + ckfree((char*)vlookup); + } + hPtr = Tcl_NextHashEntry(&place); + } + iclsPtr2 = Itcl_AdvanceHierIter(&hier); + } + Itcl_DeleteHierIter(&hier); + + /* + * Clear the command resolution table. + */ + while (1) { + hPtr = Tcl_FirstHashEntry(&iclsPtr->resolveCmds, &place); + if (hPtr == NULL) { + break; + } + clookupPtr = Tcl_GetHashValue(hPtr); + ckfree((char *)clookupPtr); + Tcl_DeleteHashEntry(hPtr); + } + Tcl_DeleteHashTable(&iclsPtr->resolveCmds); + Tcl_InitObjHashTable(&iclsPtr->resolveCmds); + + /* + * Scan through all classes in the hierarchy, from most to + * least specific. Look for the first (most-specific) definition + * of each member function, and enter it into the table. + */ + Itcl_InitHierIter(&hier, iclsPtr); + iclsPtr2 = Itcl_AdvanceHierIter(&hier); + while (iclsPtr2 != NULL) { + hPtr = Tcl_FirstHashEntry(&iclsPtr2->functions, &place); + while (hPtr) { + imPtr = (ItclMemberFunc*)Tcl_GetHashValue(hPtr); + + /* + * Create all possible names for this function and enter + * them into the command resolution table: + * func + * class::func + * namesp1::class::func + * namesp2::namesp1::class::func + * ... + */ + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, Tcl_GetString(imPtr->namePtr), -1); + nsPtr = iclsPtr2->nsPtr; + + while (1) { + objPtr = Tcl_NewStringObj(Tcl_DStringValue(&buffer), -1); + hPtr = Tcl_CreateHashEntry(&iclsPtr->resolveCmds, + (char *)objPtr, &newEntry); + + if (newEntry) { + clookupPtr = (ItclCmdLookup *)ckalloc(sizeof(ItclCmdLookup)); + memset(clookupPtr, 0, sizeof(ItclCmdLookup)); + clookupPtr->imPtr = imPtr; + Tcl_SetHashValue(hPtr, (ClientData)clookupPtr); +#ifdef NEW_PROTO_RESOLVER + int type = CMD_TYPE_METHOD; + if (imPtr->flags & ITCL_COMMON) { + type = CMD_TYPE_PROC; + } + icciPtr = (ItclClassCmdInfo *)ckalloc( + sizeof(ItclClassCmdInfo)); + icciPtr->type = type; + icciPtr->protection = imPtr->protection; + icciPtr->nsPtr = iclsPtr->nsPtr; + icciPtr->declaringNsPtr = iclsPtr2->nsPtr; + ClientData clientData2; + clientData2 = Itcl_RegisterClassCommand( + iclsPtr->infoPtr->interp, iclsPtr->nsPtr, + Tcl_GetString(imPtr->namePtr), icciPtr); + clookupPtr->classCmdInfoPtr = clientData2; + clookupPtr->cmdPtr = imPtr->accessCmd; +#endif + } else { + Tcl_DecrRefCount(objPtr); + } + + if (nsPtr == NULL) { + break; + } + Tcl_DStringSetLength(&buffer2, 0); + Tcl_DStringAppend(&buffer2, Tcl_DStringValue(&buffer), -1); + Tcl_DStringSetLength(&buffer, 0); + Tcl_DStringAppend(&buffer, nsPtr->name, -1); + Tcl_DStringAppend(&buffer, "::", -1); + Tcl_DStringAppend(&buffer, Tcl_DStringValue(&buffer2), -1); + + nsPtr = nsPtr->parentPtr; + } + hPtr = Tcl_NextHashEntry(&place); + } + iclsPtr2 = Itcl_AdvanceHierIter(&hier); + } + Itcl_DeleteHierIter(&hier); + + /* + * Scan through all classes in the hierarchy, from most to + * least specific. Look for the first (most-specific) definition + * of each delegated member function, and enter it into the table. + */ + Itcl_InitHierIter(&hier, iclsPtr); + iclsPtr2 = Itcl_AdvanceHierIter(&hier); + while (iclsPtr2 != NULL) { + hPtr = Tcl_FirstHashEntry(&iclsPtr2->delegatedFunctions, &place); + while (hPtr) { + idmPtr = (ItclDelegatedFunction *)Tcl_GetHashValue(hPtr); + hPtr = Tcl_FindHashEntry(&iclsPtr->delegatedFunctions, + (char *)idmPtr->namePtr); + if (hPtr == NULL) { + hPtr = Tcl_CreateHashEntry(&iclsPtr->delegatedFunctions, + (char *)idmPtr->namePtr, &newEntry); + Tcl_SetHashValue(hPtr, idmPtr); + } + hPtr = Tcl_NextHashEntry(&place); + } + iclsPtr2 = Itcl_AdvanceHierIter(&hier); + } + Itcl_DeleteHierIter(&hier); + + Tcl_DStringFree(&buffer); + Tcl_DStringFree(&buffer2); +} + + +/* + * ------------------------------------------------------------------------ + * Itcl_CreateVariable() + * + * Creates a new class variable definition. If this is a public + * variable, it may have a bit of "config" code that is used to + * update the object whenever the variable is modified via the + * built-in "configure" method. + * + * Returns TCL_ERROR along with an error message in the specified + * interpreter if anything goes wrong. Otherwise, this returns + * TCL_OK and a pointer to the new variable definition in "ivPtr". + * ------------------------------------------------------------------------ + */ +int +Itcl_CreateVariable( + Tcl_Interp *interp, /* interpreter managing this transaction */ + ItclClass* iclsPtr, /* class containing this variable */ + Tcl_Obj* namePtr, /* variable name */ + char* init, /* initial value */ + char* config, /* code invoked when variable is configured */ + ItclVariable** ivPtrPtr) /* returns: new variable definition */ +{ + int newEntry; + ItclVariable *ivPtr; + ItclMemberCode *mCodePtr; + Tcl_HashEntry *hPtr; + + /* + * Add this variable to the variable table for the class. + * Make sure that the variable name does not already exist. + */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->variables, (char *)namePtr, &newEntry); + if (!newEntry) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "variable name \"", Tcl_GetString(namePtr), + "\" already defined in class \"", + Tcl_GetString(iclsPtr->fullNamePtr), "\"", + (char*)NULL); + return TCL_ERROR; + } + + /* + * If this variable has some "config" code, try to capture + * its implementation. + */ + if (config) { + if (Itcl_CreateMemberCode(interp, iclsPtr, (char*)NULL, config, + &mCodePtr) != TCL_OK) { + Tcl_DeleteHashEntry(hPtr); + return TCL_ERROR; + } + ItclPreserveMemberCode(mCodePtr); + } else { + mCodePtr = NULL; + } + + + /* + * If everything looks good, create the variable definition. + */ + ivPtr = (ItclVariable*)ckalloc(sizeof(ItclVariable)); + memset(ivPtr, 0, sizeof(ItclVariable)); + ivPtr->iclsPtr = iclsPtr; + ivPtr->infoPtr = iclsPtr->infoPtr; + ivPtr->protection = Itcl_Protection(interp, 0); + ivPtr->codePtr = mCodePtr; + ivPtr->namePtr = namePtr; + Tcl_IncrRefCount(ivPtr->namePtr); + ivPtr->fullNamePtr = Tcl_NewStringObj( + Tcl_GetString(iclsPtr->fullNamePtr), -1); + Tcl_AppendToObj(ivPtr->fullNamePtr, "::", 2); + Tcl_AppendToObj(ivPtr->fullNamePtr, Tcl_GetString(namePtr), -1); + Tcl_IncrRefCount(ivPtr->fullNamePtr); + + if (ivPtr->protection == ITCL_DEFAULT_PROTECT) { + ivPtr->protection = ITCL_PROTECTED; + } + + if (init != NULL) { + ivPtr->init = Tcl_NewStringObj(init, -1); + Tcl_IncrRefCount(ivPtr->init); + } else { + ivPtr->init = NULL; + } + + Tcl_SetHashValue(hPtr, (ClientData)ivPtr); + Itcl_PreserveData((ClientData)ivPtr); + Itcl_EventuallyFree((ClientData)ivPtr, Itcl_DeleteVariable); + + *ivPtrPtr = ivPtr; + return TCL_OK; +} + +/* + * ------------------------------------------------------------------------ + * Itcl_CreateOption() + * + * Creates a new class option definition. If this is a public + * option, it may have a bit of "config" code that is used to + * update the object whenever the option is modified via the + * built-in "configure" method. + * + * Returns TCL_ERROR along with an error message in the specified + * interpreter if anything goes wrong. Otherwise, this returns + * TCL_OK and a pointer to the new option definition in "ioptPtr". + * ------------------------------------------------------------------------ + */ +int +Itcl_CreateOption( + Tcl_Interp *interp, /* interpreter managing this transaction */ + ItclClass* iclsPtr, /* class containing this variable */ + ItclOption* ioptPtr) /* new option definition */ +{ + int newEntry; + Tcl_HashEntry *hPtr; + + /* + * Add this option to the options table for the class. + * Make sure that the option name does not already exist. + */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->options, + (char *)ioptPtr->namePtr, &newEntry); + if (!newEntry) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "option name \"", Tcl_GetString(ioptPtr->namePtr), + "\" already defined in class \"", + Tcl_GetString(iclsPtr->fullNamePtr), "\"", + (char*)NULL); + return TCL_ERROR; + } + + iclsPtr->numOptions++; + ioptPtr->iclsPtr = iclsPtr; + ioptPtr->codePtr = NULL; + ioptPtr->fullNamePtr = Tcl_NewStringObj( + Tcl_GetString(iclsPtr->fullNamePtr), -1); + Tcl_AppendToObj(ioptPtr->fullNamePtr, "::", 2); + Tcl_AppendToObj(ioptPtr->fullNamePtr, Tcl_GetString(ioptPtr->namePtr), -1); + Tcl_IncrRefCount(ioptPtr->fullNamePtr); + Tcl_SetHashValue(hPtr, (ClientData)ioptPtr); + Itcl_PreserveData((ClientData)ioptPtr); + Itcl_EventuallyFree((ClientData)ioptPtr, ItclDeleteOption); + return TCL_OK; +} + +/* + * ------------------------------------------------------------------------ + * Itcl_CreateMethodVariable() + * + * Creates a new class methdovariable definition. If this is a public + * methodvariable, + * + * Returns TCL_ERROR along with an error message in the specified + * interpreter if anything goes wrong. Otherwise, this returns + * TCL_OK and a pointer to the new option definition in "imvPtr". + * ------------------------------------------------------------------------ + */ +int +Itcl_CreateMethodVariable( + Tcl_Interp *interp, /* interpreter managing this transaction */ + ItclClass* iclsPtr, /* class containing this variable */ + Tcl_Obj* namePtr, /* variable name */ + Tcl_Obj* defaultPtr, /* initial value */ + Tcl_Obj* callbackPtr, /* code invoked when variable is set */ + ItclMethodVariable** imvPtrPtr) + /* returns: new methdovariable definition */ +{ + int isNew; + ItclMethodVariable *imvPtr; + Tcl_HashEntry *hPtr; + + /* + * Add this methodvariable to the options table for the class. + * Make sure that the methodvariable name does not already exist. + */ + hPtr = Tcl_CreateHashEntry(&iclsPtr->methodVariables, + (char *)namePtr, &isNew); + if (!isNew) { + Tcl_AppendStringsToObj(Tcl_GetObjResult(interp), + "methdovariable name \"", Tcl_GetString(namePtr), + "\" already defined in class \"", + Tcl_GetString (iclsPtr->fullNamePtr), "\"", + (char*)NULL); + return TCL_ERROR; + } + Tcl_IncrRefCount(namePtr); + + /* + * If everything looks good, create the option definition. + */ + imvPtr = (ItclMethodVariable*)ckalloc(sizeof(ItclMethodVariable)); + memset(imvPtr, 0, sizeof(ItclMethodVariable)); + imvPtr->iclsPtr = iclsPtr; + imvPtr->protection = Itcl_Protection(interp, 0); + imvPtr->namePtr = namePtr; + Tcl_IncrRefCount(imvPtr->namePtr); + imvPtr->fullNamePtr = Tcl_NewStringObj( + Tcl_GetString(iclsPtr->fullNamePtr), -1); + Tcl_AppendToObj(imvPtr->fullNamePtr, "::", 2); + Tcl_AppendToObj(imvPtr->fullNamePtr, Tcl_GetString(namePtr), -1); + Tcl_IncrRefCount(imvPtr->fullNamePtr); + imvPtr->defaultValuePtr = defaultPtr; + if (defaultPtr != NULL) { + Tcl_IncrRefCount(imvPtr->defaultValuePtr); + } + imvPtr->callbackPtr = callbackPtr; + if (callbackPtr != NULL) { + Tcl_IncrRefCount(imvPtr->callbackPtr); + } + + if (imvPtr->protection == ITCL_DEFAULT_PROTECT) { + imvPtr->protection = ITCL_PROTECTED; + } + + Tcl_SetHashValue(hPtr, (ClientData)imvPtr); + + *imvPtrPtr = imvPtr; + return TCL_OK; +} + + + +/* + * ------------------------------------------------------------------------ + * Itcl_GetCommonVar() + * + * Returns the current value for a common class variable. The member + * name is interpreted with respect to the given class scope. That + * scope is installed as the current context before querying the + * variable. This by-passes the protection level in case the variable + * is "private". + * + * If successful, this procedure returns a pointer to a string value + * which remains alive until the variable changes it value. If + * anything goes wrong, this returns NULL. + * ------------------------------------------------------------------------ + */ +const char* +Itcl_GetCommonVar( + Tcl_Interp *interp, /* current interpreter */ + const char *name, /* name of desired instance variable */ + ItclClass *contextIclsPtr) /* name is interpreted in this scope */ +{ + const char *val = NULL; + Tcl_HashEntry *hPtr; + Tcl_DString buffer; + Tcl_Obj *namePtr; + ItclVariable *ivPtr; + const char *cp; + const char *lastCp; + Tcl_Object oPtr = NULL; + + lastCp = name; + cp = name; + while (cp != NULL) { + cp = strstr(lastCp, "::"); + if (cp != NULL) { + lastCp = cp + 2; + } + } + namePtr = Tcl_NewStringObj(lastCp, -1); + Tcl_IncrRefCount(namePtr); + hPtr = Tcl_FindHashEntry(&contextIclsPtr->variables, (char *)namePtr); + Tcl_DecrRefCount(namePtr); + if (hPtr == NULL) { + return NULL; + } + ivPtr = Tcl_GetHashValue(hPtr); + /* + * Activate the namespace for the given class. That installs + * the appropriate name resolution rules and by-passes any + * security restrictions. + */ + + if (lastCp == name) { + /* 'name' is a simple name (this is untested!!!!) */ + + /* Use the context class passed in */ + oPtr = contextIclsPtr->oPtr; + + } else { + int code = TCL_ERROR; + Tcl_Obj *classObjPtr = Tcl_NewStringObj(name, lastCp - name - 2); + oPtr = Tcl_GetObjectFromObj(interp, classObjPtr); + + if (oPtr) { + ItclClass *iclsPtr = Tcl_ObjectGetMetadata(oPtr, + contextIclsPtr->infoPtr->class_meta_type); + if (iclsPtr) { + + code = TCL_OK; + assert(oPtr == iclsPtr->oPtr); + + /* + * If the caller gave us a qualified name into + * somewhere other than the context class, then + * things are really weird. Consider an assertion + * to prevent, but for now keep the functioning + * unchanged. + * + * assert(iclsPtr == contextIclsPtr); + */ + + } + + } + Tcl_DecrRefCount(classObjPtr); + if (code != TCL_OK) { + return NULL; + } + + } + + Tcl_DStringInit(&buffer); + if (ivPtr->protection != ITCL_PUBLIC) { + Tcl_DStringAppend(&buffer, ITCL_VARIABLES_NAMESPACE, -1); + } + Tcl_DStringAppend(&buffer, (Tcl_GetObjectNamespace(oPtr))->fullName, -1); + Tcl_DStringAppend(&buffer, "::", -1); + Tcl_DStringAppend(&buffer, lastCp, -1); + + val = Tcl_GetVar2(interp, (const char *)Tcl_DStringValue(&buffer), + (char*)NULL, 0); + Tcl_DStringFree(&buffer); + return val; +} + + +/* + * ------------------------------------------------------------------------ + * Itcl_InitHierIter() + * + * Initializes an iterator for traversing the hierarchy of the given + * class. Subsequent calls to Itcl_AdvanceHierIter() will return + * the base classes in order from most-to-least specific. + * ------------------------------------------------------------------------ + */ +void +Itcl_InitHierIter(iter,iclsPtr) + ItclHierIter *iter; /* iterator used for traversal */ + ItclClass *iclsPtr; /* class definition for start of traversal */ +{ + Itcl_InitStack(&iter->stack); + Itcl_PushStack((ClientData)iclsPtr, &iter->stack); + iter->current = iclsPtr; +} + +/* + * ------------------------------------------------------------------------ + * Itcl_DeleteHierIter() + * + * Destroys an iterator for traversing class hierarchies, freeing + * all memory associated with it. + * ------------------------------------------------------------------------ + */ +void +Itcl_DeleteHierIter(iter) + ItclHierIter *iter; /* iterator used for traversal */ +{ + Itcl_DeleteStack(&iter->stack); + iter->current = NULL; +} + +/* + * ------------------------------------------------------------------------ + * Itcl_AdvanceHierIter() + * + * Moves a class hierarchy iterator forward to the next base class. + * Returns a pointer to the current class definition, or NULL when + * the end of the hierarchy has been reached. + * ------------------------------------------------------------------------ + */ +ItclClass* +Itcl_AdvanceHierIter( + ItclHierIter *iter) /* iterator used for traversal */ +{ + register Itcl_ListElem *elem; + ItclClass *iclsPtr; + + iter->current = (ItclClass*)Itcl_PopStack(&iter->stack); + + /* + * Push classes onto the stack in reverse order, so that + * they will be popped off in the proper order. + */ + if (iter->current) { + iclsPtr = (ItclClass*)iter->current; + elem = Itcl_LastListElem(&iclsPtr->bases); + while (elem) { + Itcl_PushStack(Itcl_GetListValue(elem), &iter->stack); + elem = Itcl_PrevListElem(elem); + } + } + return iter->current; +} + +/* + * ------------------------------------------------------------------------ + * Itcl_DeleteVariable() + * + * Destroys a variable definition created by Itcl_CreateVariable(), + * freeing all resources associated with it. + * ------------------------------------------------------------------------ + */ +void +Itcl_DeleteVariable( + char *cdata) /* variable definition to be destroyed */ +{ + Tcl_HashEntry *hPtr; + ItclVariable *ivPtr; + + ivPtr = (ItclVariable *)cdata; +if (ivPtr->arrayInitPtr != NULL) { +} + hPtr = Tcl_FindHashEntry(&ivPtr->infoPtr->classes, (char *)ivPtr->iclsPtr); + if (hPtr != NULL) { + /* unlink owerself from list of class variables */ + hPtr = Tcl_FindHashEntry(&ivPtr->iclsPtr->variables, + (char *)ivPtr->namePtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } + } + if (ivPtr->codePtr != NULL) { + ItclReleaseMemberCode(ivPtr->codePtr); + } + Tcl_DecrRefCount(ivPtr->namePtr); + Tcl_DecrRefCount(ivPtr->fullNamePtr); + if (ivPtr->init) { + Tcl_DecrRefCount(ivPtr->init); + } + if (ivPtr->arrayInitPtr) { + Tcl_DecrRefCount(ivPtr->arrayInitPtr); + } + ckfree((char*)ivPtr); +} + +/* + * ------------------------------------------------------------------------ + * ItclDeleteOption() + * + * Destroys a option definition created by Itcl_CreateOption(), + * freeing all resources associated with it. + * ------------------------------------------------------------------------ + */ +static void +ItclDeleteOption( + char *cdata) /* option definition to be destroyed */ +{ + ItclOption *ioptPtr; + + ioptPtr = (ItclOption *)cdata; + Tcl_DecrRefCount(ioptPtr->namePtr); + Tcl_DecrRefCount(ioptPtr->fullNamePtr); + if (ioptPtr->resourceNamePtr != NULL) { + Tcl_DecrRefCount(ioptPtr->resourceNamePtr); + } + if (ioptPtr->resourceNamePtr != NULL) { + Tcl_DecrRefCount(ioptPtr->classNamePtr); + } + + if (ioptPtr->codePtr) { + ItclReleaseMemberCode(ioptPtr->codePtr); + } + if (ioptPtr->defaultValuePtr != NULL) { + Tcl_DecrRefCount(ioptPtr->defaultValuePtr); + } + if (ioptPtr->cgetMethodPtr != NULL) { + Tcl_DecrRefCount(ioptPtr->cgetMethodPtr); + } + if (ioptPtr->cgetMethodVarPtr != NULL) { + Tcl_DecrRefCount(ioptPtr->cgetMethodVarPtr); + } + if (ioptPtr->configureMethodPtr != NULL) { + Tcl_DecrRefCount(ioptPtr->configureMethodPtr); + } + if (ioptPtr->configureMethodVarPtr != NULL) { + Tcl_DecrRefCount(ioptPtr->configureMethodVarPtr); + } + if (ioptPtr->validateMethodPtr != NULL) { + Tcl_DecrRefCount(ioptPtr->validateMethodPtr); + } + if (ioptPtr->validateMethodVarPtr != NULL) { + Tcl_DecrRefCount(ioptPtr->validateMethodVarPtr); + } + Itcl_ReleaseData(ioptPtr->idoPtr); + ckfree((char*)ioptPtr); +} + +/* + * ------------------------------------------------------------------------ + * ItclDeleteFunction() + * + * fre data associated with a function + * ------------------------------------------------------------------------ + */ +static void +ItclDeleteFunction( + ItclMemberFunc *imPtr) +{ + Tcl_HashEntry *hPtr; + +if (imPtr->iclsPtr) { + hPtr = Tcl_FindHashEntry(&imPtr->iclsPtr->infoPtr->procMethods, + (char *) imPtr->tmPtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } +} + hPtr = Tcl_FindHashEntry(&imPtr->infoPtr->classes, (char *)imPtr->iclsPtr); + if (hPtr != NULL) { + /* unlink owerself from list of class functions */ + hPtr = Tcl_FindHashEntry(&imPtr->iclsPtr->functions, + (char *)imPtr->namePtr); + if (hPtr != NULL) { + Tcl_DeleteHashEntry(hPtr); + } + } + if (imPtr->codePtr != NULL) { + ItclReleaseMemberCode(imPtr->codePtr); + } + Tcl_DecrRefCount(imPtr->namePtr); + Tcl_DecrRefCount(imPtr->fullNamePtr); + if (imPtr->usagePtr != NULL) { + Tcl_DecrRefCount(imPtr->usagePtr); + } + if (imPtr->argumentPtr != NULL) { + Tcl_DecrRefCount(imPtr->argumentPtr); + } + if (imPtr->origArgsPtr != NULL) { + Tcl_DecrRefCount(imPtr->origArgsPtr); + } + if (imPtr->builtinArgumentPtr != NULL) { + Tcl_DecrRefCount(imPtr->builtinArgumentPtr); + } + if (imPtr->bodyPtr != NULL) { + Tcl_DecrRefCount(imPtr->bodyPtr); + } + if (imPtr->argListPtr != NULL) { + ItclDeleteArgList(imPtr->argListPtr); + } + ckfree((char*)imPtr); +} + +/* + * ------------------------------------------------------------------------ + * ItclDeleteComponent() + * + * free data associated with a component + * ------------------------------------------------------------------------ + */ +static void +ItclDeleteComponent( + ItclComponent *icPtr) +{ + Tcl_Obj *objPtr; + FOREACH_HASH_DECLS; + + Tcl_DecrRefCount(icPtr->namePtr); + /* the variable and the command are freed when freeing variables, + * functions */ + FOREACH_HASH_VALUE(objPtr, &icPtr->keptOptions) { + if (objPtr != NULL) { + Tcl_DecrRefCount(objPtr); + } + } + Tcl_DeleteHashTable(&icPtr->keptOptions); + ckfree((char*)icPtr); +} + +/* + * ------------------------------------------------------------------------ + * ItclDeleteDelegatedOption() + * + * free data associated with a delegated option + * ------------------------------------------------------------------------ + */ +void +ItclDeleteDelegatedOption( + char *cdata) +{ + Tcl_Obj *objPtr; + FOREACH_HASH_DECLS; + ItclDelegatedOption *idoPtr; + + idoPtr = (ItclDelegatedOption *)cdata; + Tcl_DecrRefCount(idoPtr->namePtr); + if (idoPtr->resourceNamePtr != NULL) { + Tcl_DecrRefCount(idoPtr->resourceNamePtr); + } + if (idoPtr->classNamePtr != NULL) { + Tcl_DecrRefCount(idoPtr->classNamePtr); + } + if (idoPtr->asPtr != NULL) { + Tcl_DecrRefCount(idoPtr->asPtr); + } + FOREACH_HASH_VALUE(objPtr, &idoPtr->exceptions) { + if (objPtr != NULL) { + Tcl_DecrRefCount(objPtr); + } + } + Tcl_DeleteHashTable(&idoPtr->exceptions); + ckfree((char *)idoPtr); +} + +/* + * ------------------------------------------------------------------------ + * ItclDeleteDelegatedFunction() + * + * free data associated with a delegated function + * ------------------------------------------------------------------------ + */ +void ItclDeleteDelegatedFunction( + ItclDelegatedFunction *idmPtr) +{ + Tcl_Obj *objPtr; + FOREACH_HASH_DECLS; + + Tcl_DecrRefCount(idmPtr->namePtr); + if (idmPtr->asPtr != NULL) { + Tcl_DecrRefCount(idmPtr->asPtr); + } + if (idmPtr->usingPtr != NULL) { + Tcl_DecrRefCount(idmPtr->usingPtr); + } + FOREACH_HASH_VALUE(objPtr, &idmPtr->exceptions) { + if (objPtr != NULL) { + Tcl_DecrRefCount(objPtr); + } + } + Tcl_DeleteHashTable(&idmPtr->exceptions); + ckfree((char *)idmPtr); +} |