OpenFPGA/libs/EXTERNAL/tcl8.6.12/pkgs/itcl4.2.2/generic/itclClass.c

2589 lines
80 KiB
C
Raw Normal View History

2022-06-07 11:15:20 -05:00
/*
* ------------------------------------------------------------------------
* 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;
assert(iclsPtr->refCount != 0);
if (iclsPtr->refCount-- <= 1) {
ItclFreeClass((char *) clientData);
}
}
/*
* ------------------------------------------------------------------------
* Itcl_DeleteMemberFunc()
*
* ------------------------------------------------------------------------
*/
void Itcl_DeleteMemberFunc (
void *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 = (ItclClass *)clientData;
ItclDestroyClassNamesp(iclsPtr);
ItclReleaseClass(iclsPtr);
}
/*
* ------------------------------------------------------------------------
* 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 = (ItclClass *)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);
}
}
static int
CallNewObjectInstance(
ClientData data[],
Tcl_Interp *interp,
int result)
{
ItclObjectInfo *infoPtr = (ItclObjectInfo *)data[0];
const char *path = (const char *)data[1];
Tcl_Object *oPtr = (Tcl_Object *)data[2];
Tcl_Obj *nameObjPtr = (Tcl_Obj *)data[3];
(void)result;
*oPtr = NULL;
if (infoPtr->clazzClassPtr) {
*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;
if (infoPtr->clazzObjectPtr == NULL) {
Tcl_AppendResult(interp, "oo-subsystem is deleted", NULL);
return TCL_ERROR;
}
/*
* 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,
NULL, /* flags */ 0);
if (classNs != NULL && Itcl_IsClassNamespace(classNs)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"class \"", path, "\" already exists",
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,
NULL, /* flags */ TCL_NAMESPACE_ONLY);
if (cmd != NULL && !Itcl_IsStub(cmd)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"command \"", path, "\" already exists",
NULL);
if (strstr(path,"::") == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
" in namespace \"",
Tcl_GetCurrentNamespace(interp)->fullName, "\"",
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, "\"",
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(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_STRING_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,
(void *)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),
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) {
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);
} 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, iclsPtr);
hPtr = Tcl_CreateHashEntry(&infoPtr->namespaceClasses, (char *)classNs,
&newEntry);
Tcl_SetHashValue(hPtr, iclsPtr);
if (classNs != ooNs) {
hPtr = Tcl_CreateHashEntry(&infoPtr->namespaceClasses, (char *)ooNs,
&newEntry);
Tcl_SetHashValue(hPtr, iclsPtr);
if (classNs->clientData && classNs->deleteProc) {
(*classNs->deleteProc)(classNs->clientData);
}
classNs->clientData = iclsPtr;
classNs->deleteProc = ItclDestroyClass2;
}
hPtr = Tcl_CreateHashEntry(&infoPtr->classes, (char *)iclsPtr, &newEntry);
Tcl_SetHashValue(hPtr, 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, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_TYPE_VAR; /* mark as "type" variable */
}
if (iclsPtr->flags & (ITCL_ECLASS)) {
namePtr = Tcl_NewStringObj("win", -1);
(void) Itcl_CreateVariable(interp, iclsPtr, namePtr, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_WIN_VAR; /* mark as "win" variable */
}
if (iclsPtr->flags & (ITCL_TYPE|ITCL_WIDGET|ITCL_WIDGETADAPTOR)) {
namePtr = Tcl_NewStringObj("self", -1);
(void) Itcl_CreateVariable(interp, iclsPtr, namePtr, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_SELF_VAR; /* mark as "self" variable */
namePtr = Tcl_NewStringObj("selfns", -1);
(void) Itcl_CreateVariable(interp, iclsPtr, namePtr, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_SELFNS_VAR; /* mark as "selfns" variable */
namePtr = Tcl_NewStringObj("win", -1);
(void) Itcl_CreateVariable(interp, iclsPtr, namePtr, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_WIN_VAR; /* mark as "win" variable */
}
namePtr = Tcl_NewStringObj("this", -1);
(void) Itcl_CreateVariable(interp, iclsPtr, namePtr, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_THIS_VAR; /* mark as "this" variable */
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, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_OPTIONS_VAR; /* mark as "itcl_options"
* variable */
}
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, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_OPTION_COMP_VAR; /* mark as "itcl_option_components"
* variable */
}
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, NULL,
NULL, &ivPtr);
ivPtr->protection = ITCL_PROTECTED; /* always "protected" */
ivPtr->flags |= ITCL_THIS_VAR; /* mark as "thiswin" variable */
}
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);
/* 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)
{
(void)interp;
(void)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 = (ItclClass *)data[0];
ItclObjectInfo *infoPtr = (ItclObjectInfo *)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 = (ItclClass *)data[0];
ItclObjectInfo *infoPtr = (ItclObjectInfo *)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 = (ItclObjectInfo *)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)))) {
Itcl_PreserveData(ioPtr);
Tcl_DeleteCommandFromToken(iclsPtr->interp, ioPtr->accessCmd);
ioPtr->accessCmd = NULL;
Itcl_ReleaseData(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);
}
/*
* Now there are no objects and inherited classes anymore, they could access a
* private/protected common variables, so delete the internal namespace.
*/
{
Tcl_DString buffer;
Tcl_Namespace *nsPtr;
Tcl_DStringInit(&buffer);
Tcl_DStringAppend(&buffer, ITCL_VARIABLES_NAMESPACE, -1);
Tcl_DStringAppend(&buffer,
(Tcl_GetObjectNamespace(iclsPtr->oPtr))->fullName, -1);
nsPtr = Tcl_FindNamespace(iclsPtr->interp, Tcl_DStringValue(&buffer), NULL, 0);
Tcl_DStringFree(&buffer);
if (nsPtr != NULL) {
Tcl_DeleteNamespace(nsPtr);
}
}
/*
* 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 = (ItclCmdLookup *)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 = (ItclOption *)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;
Itcl_ReleaseData(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)
|| (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 = (ItclComponent *)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 = (ItclVariable *)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(iclsPtr->infoPtr);
Tcl_DecrRefCount(iclsPtr->namePtr);
Tcl_DecrRefCount(iclsPtr->fullNamePtr);
if (iclsPtr->resolvePtr != NULL) {
ckfree((char *)iclsPtr->resolvePtr->clientData);
ckfree((char *)iclsPtr->resolvePtr);
}
ckfree(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 = (ItclObjectInfo *)
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, "\"",
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(
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 = (Tcl_Obj *)data[0];
ItclClass *iclsPtr = (ItclClass *)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 = (Tcl_Obj *)data[0];
ItclClass *iclsPtr = (ItclClass *)data[1];
int objc = PTR2INT(data[2]);
Tcl_Obj **objv = (Tcl_Obj **)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 = (ItclClass *)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?",
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;
}
if (*objName == '\0') {
Tcl_AppendResult(interp, "object name must not be empty", NULL);
Tcl_SetErrorCode(interp, "TCL", "OO", "EMPTY_NAME", NULL);
return TCL_ERROR;
}
/*
* 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);
}
/*
* ------------------------------------------------------------------------
* ItclResolveVarEntry()
*
* Side effect: (re)build part of resolver hash-table on demand.
* ------------------------------------------------------------------------
*/
Tcl_HashEntry *
ItclResolveVarEntry(
ItclClass* iclsPtr, /* class definition where to resolve variable */
const char *lookupName) /* name of variable being resolved */
{
Tcl_HashEntry *reshPtr, *hPtr;
/* could be resolved directly */
if ((reshPtr = Tcl_FindHashEntry(&iclsPtr->resolveVars, lookupName)) != NULL) {
return reshPtr;
} else {
/* try to build virtual table for this var */
const char *varName, *simpleName;
Tcl_DString buffer, buffer2, *bufferC;
ItclHierIter hier;
ItclClass* iclsPtr2;
ItclVarLookup *vlookup;
ItclVariable *ivPtr;
Tcl_Namespace* nsPtr;
Tcl_Obj *vnObjPtr;
int newEntry, processAncestors;
size_t varLen;
/* (de)qualify to simple name */
varName = simpleName = lookupName;
while(*varName) {
if (*varName++ == ':') {
if (*varName++ == ':') { simpleName = varName; }
};
}
vnObjPtr = Tcl_NewStringObj(simpleName, -1);
processAncestors = simpleName != lookupName;
Tcl_DStringInit(&buffer);
Tcl_DStringInit(&buffer2);
/*
* 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_FindHashEntry(&iclsPtr2->variables, vnObjPtr);
if (hPtr) {
ivPtr = (ItclVariable*)Tcl_GetHashValue(hPtr);
vlookup = NULL;
/*
* 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
* ...
*/
varName = simpleName; varLen = -1;
bufferC = &buffer;
nsPtr = iclsPtr2->nsPtr;
while (1) {
hPtr = Tcl_CreateHashEntry(&iclsPtr->resolveVars,
varName, &newEntry);
/* check for same name in current class */
if (!newEntry) {
vlookup = (ItclVarLookup*)Tcl_GetHashValue(hPtr);
if (vlookup->ivPtr != ivPtr && iclsPtr2 == iclsPtr) {
/* if used multiple times - unbind, else - overwrite */
if (vlookup->usage > 1) {
/* correct leastQualName */
vlookup->leastQualName = NULL;
processAncestors = 1; /* correction in progress */
/* should create new lookup */
--vlookup->usage;
vlookup = NULL;
} else {
/* correct values (overwrite) */
vlookup->usage = 0;
goto setResVar;
}
newEntry = 1;
} else {
/* var exists and no correction necessary - next var */
if (!processAncestors) {
break;
}
/* check leastQualName correction needed */
if (!vlookup->leastQualName) {
vlookup->leastQualName = (char *)
Tcl_GetHashKey(&iclsPtr->resolveVars, hPtr);
}
/* reset vlookup for full-qualified names - new lookup */
if (vlookup->ivPtr != ivPtr) {
vlookup = NULL;
}
}
}
if (newEntry) {
if (!vlookup) {
/* create new (or overwrite) */
vlookup = (ItclVarLookup *)ckalloc(sizeof(ItclVarLookup));
vlookup->usage = 0;
setResVar:
vlookup->ivPtr = ivPtr;
vlookup->leastQualName = (char *)
Tcl_GetHashKey(&iclsPtr->resolveVars, hPtr);
/*
* If this variable is PRIVATE to another class scope,
* then mark it as "inaccessible".
*/
vlookup->accessible = (ivPtr->protection != ITCL_PRIVATE ||
ivPtr->iclsPtr == iclsPtr);
/*
* 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.
*/
if (!iclsPtr->numInstanceVars) {
iclsPtr->numInstanceVars += 3;
}
/*
* 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++;
}
}
}
Tcl_SetHashValue(hPtr, vlookup);
vlookup->usage++;
}
/* if we have found it */
if (simpleName == lookupName || strcmp(varName, lookupName) == 0) {
if (!reshPtr) {
reshPtr = hPtr;
}
break;
}
if (nsPtr == NULL) {
break;
}
Tcl_DStringSetLength(bufferC, 0);
Tcl_DStringAppend(bufferC, nsPtr->name, -1);
Tcl_DStringAppend(bufferC, "::", 2);
Tcl_DStringAppend(bufferC, varName, varLen);
varName = Tcl_DStringValue(bufferC);
varLen = Tcl_DStringLength(bufferC);
bufferC = (bufferC == &buffer) ? &buffer2 : &buffer;
nsPtr = nsPtr->parentPtr;
}
}
/* Stop create vars for ancestors (if not needed) */
if (!processAncestors && reshPtr) {
/* simple name - don't need to check ancestors */
break;
}
iclsPtr2 = Itcl_AdvanceHierIter(&hier);
}
Itcl_DeleteHierIter(&hier);
Tcl_DStringFree(&buffer);
Tcl_DStringFree(&buffer2);
Tcl_DecrRefCount(vnObjPtr);
if (reshPtr == NULL) {
reshPtr = Tcl_FindHashEntry(&iclsPtr->resolveVars, lookupName);
}
return reshPtr;
}
}
/*
* ------------------------------------------------------------------------
* 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 (built on demand, moved to ItclResolveVarEntry)
* 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, *bufferC, *bufferC2, *bufferSwp;
Tcl_Obj *objPtr;
ItclMemberFunc *imPtr;
ItclDelegatedFunction *idmPtr;
ItclHierIter hier;
ItclClass *iclsPtr2;
ItclCmdLookup *clookupPtr;
int newEntry;
Tcl_DStringInit(&buffer);
Tcl_DStringInit(&buffer2);
/*
* Clear the command resolution table.
*/
while (1) {
hPtr = Tcl_FirstHashEntry(&iclsPtr->resolveCmds, &place);
if (hPtr == NULL) {
break;
}
clookupPtr = (ItclCmdLookup *)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);
bufferC = &buffer; bufferC2 = &buffer2;
nsPtr = iclsPtr2->nsPtr;
while (1) {
objPtr = Tcl_NewStringObj(Tcl_DStringValue(bufferC),
Tcl_DStringLength(bufferC));
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, clookupPtr);
} else {
Tcl_DecrRefCount(objPtr);
}
if (nsPtr == NULL) {
break;
}
Tcl_DStringSetLength(bufferC2, 0);
Tcl_DStringAppend(bufferC2, nsPtr->name, -1);
Tcl_DStringAppend(bufferC2, "::", 2);
Tcl_DStringAppend(bufferC2, Tcl_DStringValue(bufferC),
Tcl_DStringLength(bufferC));
bufferSwp = bufferC; bufferC = bufferC2; bufferC2 = bufferSwp;
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), "\"",
NULL);
return TCL_ERROR;
}
/*
* If this variable has some "config" code, try to capture
* its implementation.
*/
if (config) {
if (Itcl_CreateMemberCode(interp, iclsPtr, NULL, config,
&mCodePtr) != TCL_OK) {
Tcl_DeleteHashEntry(hPtr);
return TCL_ERROR;
}
Itcl_PreserveData(mCodePtr);
} else {
mCodePtr = NULL;
}
/*
* If everything looks good, create the variable definition.
*/
ivPtr = (ItclVariable*)Itcl_Alloc(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, ivPtr);
Itcl_PreserveData(ivPtr);
Itcl_EventuallyFree(ivPtr, (Tcl_FreeProc *) 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), "\"",
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, ioptPtr);
Itcl_PreserveData(ioptPtr);
Itcl_EventuallyFree(ioptPtr, (Tcl_FreeProc *) ItclDeleteOption);
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* ItclCreateMethodVariable()
*
* 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
ItclCreateMethodVariable(
Tcl_Interp *interp, /* interpreter managing this transaction */
ItclVariable *ivPtr, /* variable reference (from Itcl_CreateVariable) */
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(&ivPtr->iclsPtr->methodVariables,
(char *)ivPtr->namePtr, &isNew);
if (!isNew) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"methdovariable name \"", Tcl_GetString(ivPtr->namePtr),
"\" already defined in class \"",
Tcl_GetString (ivPtr->iclsPtr->fullNamePtr), "\"",
NULL);
return TCL_ERROR;
}
/*
* If everything looks good, create the option definition.
*/
imvPtr = (ItclMethodVariable*)ckalloc(sizeof(ItclMethodVariable));
memset(imvPtr, 0, sizeof(ItclMethodVariable));
imvPtr->iclsPtr = ivPtr->iclsPtr;
imvPtr->protection = Itcl_Protection(interp, 0);
imvPtr->namePtr = ivPtr->namePtr;
Tcl_IncrRefCount(imvPtr->namePtr);
imvPtr->fullNamePtr = ivPtr->fullNamePtr;
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, 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 = (ItclVariable *)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 = (ItclClass *)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),
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(
ItclHierIter *iter, /* iterator used for traversal */
ItclClass *iclsPtr) /* class definition for start of traversal */
{
Itcl_InitStack(&iter->stack);
Itcl_PushStack(iclsPtr, &iter->stack);
iter->current = iclsPtr;
}
/*
* ------------------------------------------------------------------------
* Itcl_DeleteHierIter()
*
* Destroys an iterator for traversing class hierarchies, freeing
* all memory associated with it.
* ------------------------------------------------------------------------
*/
void
Itcl_DeleteHierIter(
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 */
{
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;
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) {
Itcl_ReleaseData(ivPtr->codePtr);
}
Tcl_DecrRefCount(ivPtr->namePtr);
Tcl_DecrRefCount(ivPtr->fullNamePtr);
if (ivPtr->init) {
Tcl_DecrRefCount(ivPtr->init);
}
if (ivPtr->arrayInitPtr) {
Tcl_DecrRefCount(ivPtr->arrayInitPtr);
}
Itcl_Free(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) {
Itcl_ReleaseData(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);
Itcl_Free(ioptPtr);
}
/*
* ------------------------------------------------------------------------
* ItclDeleteFunction()
*
* fre data associated with a function
* ------------------------------------------------------------------------
*/
static void
ItclDeleteFunction(
ItclMemberFunc *imPtr)
{
Tcl_HashEntry *hPtr;
hPtr = Tcl_FindHashEntry(&imPtr->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) {
Itcl_ReleaseData(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);
}
Itcl_Free(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);
Itcl_Free(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);
}