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

2719 lines
86 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* ------------------------------------------------------------------------
* 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 commands available within a class scope.
* In [incr Tcl], the term "method" is used for a procedure that has
* access to object-specific data, while the term "proc" is used for
* a procedure that has access only to common class data.
*
* ========================================================================
* 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) 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 int EquivArgLists(Tcl_Interp *interp, ItclArgList *origArgs,
ItclArgList *realArgs);
static int ItclCreateMemberCode(Tcl_Interp* interp, ItclClass *iclsPtr,
const char* arglist, const char* body, ItclMemberCode** mcodePtr,
Tcl_Obj *namePtr, int flags);
static int ItclCreateMemberFunc(Tcl_Interp* interp, ItclClass *iclsPtr,
Tcl_Obj *namePtr, const char* arglist, const char* body,
ItclMemberFunc** imPtrPtr, int flags);
static void FreeMemberCode(ItclMemberCode *mcodePtr);
/*
* ------------------------------------------------------------------------
* Itcl_BodyCmd()
*
* Invoked by Tcl whenever the user issues an "itcl::body" command to
* define or redefine the implementation for a class method/proc.
* Handles the following syntax:
*
* itcl::body <class>::<func> <arglist> <body>
*
* Looks for an existing class member function with the name <func>,
* and if found, tries to assign the implementation. If an argument
* list was specified in the original declaration, it must match
* <arglist> or an error is flagged. If <body> has the form "@name"
* then it is treated as a reference to a C handling procedure;
* otherwise, it is taken as a body of Tcl statements.
*
* Returns TCL_OK/TCL_ERROR to indicate success/failure.
* ------------------------------------------------------------------------
*/
static int
NRBodyCmd(
TCL_UNUSED(ClientData), /* */
Tcl_Interp *interp, /* current interpreter */
int objc, /* number of arguments */
Tcl_Obj *const *objv) /* argument objects */
{
Tcl_HashEntry *entry;
Tcl_DString buffer;
Tcl_Obj *objPtr;
ItclClass *iclsPtr;
ItclMemberFunc *imPtr;
const char *head;
const char *tail;
const char *token;
char *arglist;
char *body;
int status = TCL_OK;
ItclShowArgs(2, "Itcl_BodyCmd", objc, objv);
if (objc != 4) {
token = Tcl_GetString(objv[0]);
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"wrong # args: should be \"",
token, " class::func arglist body\"",
NULL);
return TCL_ERROR;
}
/*
* Parse the member name "namesp::namesp::class::func".
* Make sure that a class name was specified, and that the
* class exists.
*/
token = Tcl_GetString(objv[1]);
Itcl_ParseNamespPath(token, &buffer, &head, &tail);
if (!head || *head == '\0') {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"missing class specifier for body declaration \"", token, "\"",
NULL);
status = TCL_ERROR;
goto bodyCmdDone;
}
iclsPtr = Itcl_FindClass(interp, head, /* autoload */ 1);
if (iclsPtr == NULL) {
status = TCL_ERROR;
goto bodyCmdDone;
}
/*
* Find the function and try to change its implementation.
* Note that command resolution table contains *all* functions,
* even those in a base class. Make sure that the class
* containing the method definition is the requested class.
*/
imPtr = NULL;
objPtr = Tcl_NewStringObj(tail, -1);
entry = Tcl_FindHashEntry(&iclsPtr->resolveCmds, (char *)objPtr);
Tcl_DecrRefCount(objPtr);
if (entry) {
ItclCmdLookup *clookup;
clookup = (ItclCmdLookup *)Tcl_GetHashValue(entry);
imPtr = clookup->imPtr;
if (imPtr->iclsPtr != iclsPtr) {
imPtr = NULL;
}
}
if (imPtr == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"function \"", tail, "\" is not defined in class \"",
Tcl_GetString(iclsPtr->fullNamePtr), "\"",
NULL);
status = TCL_ERROR;
goto bodyCmdDone;
}
arglist = Tcl_GetString(objv[2]);
body = Tcl_GetString(objv[3]);
if (Itcl_ChangeMemberFunc(interp, imPtr, arglist, body) != TCL_OK) {
status = TCL_ERROR;
goto bodyCmdDone;
}
bodyCmdDone:
Tcl_DStringFree(&buffer);
return status;
}
/* ARGSUSED */
int
Itcl_BodyCmd(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const *objv)
{
return Tcl_NRCallObjProc(interp, NRBodyCmd, clientData, objc, objv);
}
/*
* ------------------------------------------------------------------------
* Itcl_ConfigBodyCmd()
*
* Invoked by Tcl whenever the user issues an "itcl::configbody" command
* to define or redefine the configuration code associated with a
* public variable. Handles the following syntax:
*
* itcl::configbody <class>::<publicVar> <body>
*
* Looks for an existing public variable with the name <publicVar>,
* and if found, tries to assign the implementation. If <body> has
* the form "@name" then it is treated as a reference to a C handling
* procedure; otherwise, it is taken as a body of Tcl statements.
*
* Returns TCL_OK/TCL_ERROR to indicate success/failure.
* ------------------------------------------------------------------------
*/
/* ARGSUSED */
static int
NRConfigBodyCmd(
TCL_UNUSED(ClientData), /* unused */
Tcl_Interp *interp, /* current interpreter */
int objc, /* number of arguments */
Tcl_Obj *const objv[]) /* argument objects */
{
int status = TCL_OK;
const char *head;
const char *tail;
const char *token;
Tcl_DString buffer;
ItclClass *iclsPtr;
ItclVarLookup *vlookup;
ItclVariable *ivPtr;
ItclMemberCode *mcode;
Tcl_HashEntry *entry;
ItclShowArgs(2, "Itcl_ConfigBodyCmd", objc, objv);
if (objc != 3) {
Tcl_WrongNumArgs(interp, 1, objv, "class::option body");
return TCL_ERROR;
}
/*
* Parse the member name "namesp::namesp::class::option".
* Make sure that a class name was specified, and that the
* class exists.
*/
token = Tcl_GetString(objv[1]);
Itcl_ParseNamespPath(token, &buffer, &head, &tail);
if ((head == NULL) || (*head == '\0')) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"missing class specifier for body declaration \"", token, "\"",
NULL);
status = TCL_ERROR;
goto configBodyCmdDone;
}
iclsPtr = Itcl_FindClass(interp, head, /* autoload */ 1);
if (iclsPtr == NULL) {
status = TCL_ERROR;
goto configBodyCmdDone;
}
/*
* Find the variable and change its implementation.
* Note that variable resolution table has *all* variables,
* even those in a base class. Make sure that the class
* containing the variable definition is the requested class.
*/
vlookup = NULL;
entry = ItclResolveVarEntry(iclsPtr, tail);
if (entry) {
vlookup = (ItclVarLookup*)Tcl_GetHashValue(entry);
if (vlookup->ivPtr->iclsPtr != iclsPtr) {
vlookup = NULL;
}
}
if (vlookup == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"option \"", tail, "\" is not defined in class \"",
Tcl_GetString(iclsPtr->fullNamePtr), "\"",
NULL);
status = TCL_ERROR;
goto configBodyCmdDone;
}
ivPtr = vlookup->ivPtr;
if (ivPtr->protection != ITCL_PUBLIC) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"option \"", Tcl_GetString(ivPtr->fullNamePtr),
"\" is not a public configuration option",
NULL);
status = TCL_ERROR;
goto configBodyCmdDone;
}
token = Tcl_GetString(objv[2]);
if (Itcl_CreateMemberCode(interp, iclsPtr, NULL, token,
&mcode) != TCL_OK) {
status = TCL_ERROR;
goto configBodyCmdDone;
}
Itcl_PreserveData(mcode);
if (ivPtr->codePtr) {
Itcl_ReleaseData(ivPtr->codePtr);
}
ivPtr->codePtr = mcode;
configBodyCmdDone:
Tcl_DStringFree(&buffer);
return status;
}
/* ARGSUSED */
int
Itcl_ConfigBodyCmd(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const *objv)
{
return Tcl_NRCallObjProc(interp, NRConfigBodyCmd, clientData, objc, objv);
}
/*
* ------------------------------------------------------------------------
* Itcl_CreateMethod()
*
* Installs a method into the namespace associated with a class.
* If another command with the same name is already installed, then
* it is overwritten.
*
* Returns TCL_OK on success, or TCL_ERROR (along with an error message
* in the specified interp) if anything goes wrong.
* ------------------------------------------------------------------------
*/
int
Itcl_CreateMethod(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class definition */
Tcl_Obj *namePtr, /* name of new method */
const char* arglist, /* space-separated list of arg names */
const char* body) /* body of commands for the method */
{
ItclMemberFunc *imPtr;
return ItclCreateMethod(interp, iclsPtr, namePtr, arglist, body, &imPtr);
}
/*
* ------------------------------------------------------------------------
* ItclCreateMethod()
*
* Installs a method into the namespace associated with a class.
* If another command with the same name is already installed, then
* it is overwritten.
*
* Returns TCL_OK on success, or TCL_ERROR (along with an error message
* in the specified interp) if anything goes wrong.
* ------------------------------------------------------------------------
*/
int
ItclCreateMethod(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class definition */
Tcl_Obj *namePtr, /* name of new method */
const char* arglist, /* space-separated list of arg names */
const char* body, /* body of commands for the method */
ItclMemberFunc **imPtrPtr)
{
ItclMemberFunc *imPtr;
/*
* Make sure that the method name does not contain anything
* goofy like a "::" scope qualifier.
*/
if (strstr(Tcl_GetString(namePtr),"::")) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad method name \"", Tcl_GetString(namePtr), "\"",
NULL);
Tcl_DecrRefCount(namePtr);
return TCL_ERROR;
}
/*
* Create the method definition.
*/
if (ItclCreateMemberFunc(interp, iclsPtr, namePtr, arglist, body,
&imPtr, 0) != TCL_OK) {
return TCL_ERROR;
}
imPtr->flags |= ITCL_METHOD;
if (imPtrPtr != NULL) {
*imPtrPtr = imPtr;
}
ItclAddClassFunctionDictInfo(interp, iclsPtr, imPtr);
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* Itcl_CreateProc()
*
* Installs a class proc into the namespace associated with a class.
* If another command with the same name is already installed, then
* it is overwritten. Returns TCL_OK on success, or TCL_ERROR (along
* with an error message in the specified interp) if anything goes
* wrong.
* ------------------------------------------------------------------------
*/
int
Itcl_CreateProc(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class definition */
Tcl_Obj* namePtr, /* name of new proc */
const char *arglist, /* space-separated list of arg names */
const char *body) /* body of commands for the proc */
{
ItclMemberFunc *imPtr;
/*
* Make sure that the proc name does not contain anything
* goofy like a "::" scope qualifier.
*/
if (strstr(Tcl_GetString(namePtr),"::")) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"bad proc name \"", Tcl_GetString(namePtr), "\"",
NULL);
return TCL_ERROR;
}
/*
* Create the proc definition.
*/
if (ItclCreateMemberFunc(interp, iclsPtr, namePtr, arglist,
body, &imPtr, ITCL_COMMON) != TCL_OK) {
return TCL_ERROR;
}
/*
* Mark procs as "common". This distinguishes them from methods.
*/
imPtr->flags |= ITCL_COMMON;
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* ItclCreateMemberFunc()
*
* Creates the data record representing a member function. This
* includes the argument list and the body of the function. If the
* body is of the form "@name", then it is treated as a label for
* a C procedure registered by Itcl_RegisterC().
*
* If any errors are encountered, this procedure returns TCL_ERROR
* along with an error message in the interpreter. Otherwise, it
* returns TCL_OK, and "imPtr" returns a pointer to the new
* member function.
* ------------------------------------------------------------------------
*/
static int
ItclCreateMemberFunc(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class definition */
Tcl_Obj *namePtr, /* name of new member */
const char* arglist, /* space-separated list of arg names */
const char* body, /* body of commands for the method */
ItclMemberFunc** imPtrPtr, /* returns: pointer to new method defn */
int flags)
{
int newEntry;
char *name;
ItclMemberFunc *imPtr;
ItclMemberCode *mcode;
Tcl_HashEntry *hPtr;
/*
* Add the member function to the list of functions for
* the class. Make sure that a member function with the
* same name doesn't already exist.
*/
hPtr = Tcl_CreateHashEntry(&iclsPtr->functions, (char *)namePtr, &newEntry);
if (!newEntry) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"\"", Tcl_GetString(namePtr), "\" already defined in class \"",
Tcl_GetString(iclsPtr->fullNamePtr), "\"",
NULL);
return TCL_ERROR;
}
/*
* Try to create the implementation for this command member.
*/
if (ItclCreateMemberCode(interp, iclsPtr, arglist, body,
&mcode, namePtr, flags) != TCL_OK) {
Tcl_DeleteHashEntry(hPtr);
return TCL_ERROR;
}
/*
* Allocate a member function definition and return.
*/
imPtr = (ItclMemberFunc*)Itcl_Alloc(sizeof(ItclMemberFunc));
Itcl_EventuallyFree(imPtr, (Tcl_FreeProc *)Itcl_DeleteMemberFunc);
imPtr->iclsPtr = iclsPtr;
imPtr->infoPtr = iclsPtr->infoPtr;
imPtr->protection = Itcl_Protection(interp, 0);
imPtr->namePtr = Tcl_NewStringObj(Tcl_GetString(namePtr), -1);
Tcl_IncrRefCount(imPtr->namePtr);
imPtr->fullNamePtr = Tcl_NewStringObj(
Tcl_GetString(iclsPtr->fullNamePtr), -1);
Tcl_AppendToObj(imPtr->fullNamePtr, "::", 2);
Tcl_AppendToObj(imPtr->fullNamePtr, Tcl_GetString(namePtr), -1);
Tcl_IncrRefCount(imPtr->fullNamePtr);
if (arglist != NULL) {
imPtr->origArgsPtr = Tcl_NewStringObj(arglist, -1);
Tcl_IncrRefCount(imPtr->origArgsPtr);
}
imPtr->codePtr = mcode;
Itcl_PreserveData(mcode);
if (imPtr->protection == ITCL_DEFAULT_PROTECT) {
imPtr->protection = ITCL_PUBLIC;
}
imPtr->declaringClassPtr = iclsPtr;
if (arglist) {
imPtr->flags |= ITCL_ARG_SPEC;
}
if (mcode->argListPtr) {
ItclCreateArgList(interp, arglist, &imPtr->argcount,
&imPtr->maxargcount, &imPtr->usagePtr,
&imPtr->argListPtr, imPtr, NULL);
Tcl_IncrRefCount(imPtr->usagePtr);
}
name = Tcl_GetString(namePtr);
if ((body != NULL) && (body[0] == '@')) {
/* check for builtin cget isa and configure and mark them for
* use of a different arglist "args" for TclOO !! */
imPtr->codePtr->flags |= ITCL_BUILTIN;
if (strcmp(name, "cget") == 0) {
}
if (strcmp(name, "configure") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "isa") == 0) {
}
if (strcmp(name, "createhull") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "keepcomponentoption") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "ignorecomponentoption") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "renamecomponentoption") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "addoptioncomponent") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "ignoreoptioncomponent") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "renameoptioncomponent") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "setupcomponent") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "itcl_initoptions") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "mytypemethod") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
imPtr->flags |= ITCL_COMMON;
}
if (strcmp(name, "mymethod") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "mytypevar") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
imPtr->flags |= ITCL_COMMON;
}
if (strcmp(name, "myvar") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "itcl_hull") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
imPtr->flags |= ITCL_COMPONENT;
}
if (strcmp(name, "callinstance") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "getinstancevar") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "myproc") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
imPtr->flags |= ITCL_COMMON;
}
if (strcmp(name, "installhull") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "destroy") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "installcomponent") == 0) {
imPtr->argcount = 0;
imPtr->maxargcount = -1;
}
if (strcmp(name, "info") == 0) {
imPtr->flags |= ITCL_COMMON;
}
}
if (strcmp(name, "constructor") == 0) {
/*
* REVISE mcode->bodyPtr here!
* Include a [my ItclConstructBase $iclsPtr] method call.
* Inherited from itcl::Root
*/
Tcl_Obj *newBody = Tcl_NewStringObj("", -1);
Tcl_AppendToObj(newBody,
"[::info object namespace ${this}]::my ItclConstructBase ", -1);
Tcl_AppendObjToObj(newBody, iclsPtr->fullNamePtr);
Tcl_AppendToObj(newBody, "\n", -1);
Tcl_AppendObjToObj(newBody, mcode->bodyPtr);
Tcl_DecrRefCount(mcode->bodyPtr);
mcode->bodyPtr = newBody;
Tcl_IncrRefCount(mcode->bodyPtr);
imPtr->flags |= ITCL_CONSTRUCTOR;
}
if (strcmp(name, "destructor") == 0) {
imPtr->flags |= ITCL_DESTRUCTOR;
}
Tcl_SetHashValue(hPtr, imPtr);
Itcl_PreserveData(imPtr);
*imPtrPtr = imPtr;
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* Itcl_CreateMemberFunc()
*
* Creates the data record representing a member function. This
* includes the argument list and the body of the function. If the
* body is of the form "@name", then it is treated as a label for
* a C procedure registered by Itcl_RegisterC().
*
* If any errors are encountered, this procedure returns TCL_ERROR
* along with an error message in the interpreter. Otherwise, it
* returns TCL_OK, and "imPtr" returns a pointer to the new
* member function.
* ------------------------------------------------------------------------
*/
int
Itcl_CreateMemberFunc(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class definition */
Tcl_Obj *namePtr, /* name of new member */
const char* arglist, /* space-separated list of arg names */
const char* body, /* body of commands for the method */
ItclMemberFunc** imPtrPtr) /* returns: pointer to new method defn */
{
return ItclCreateMemberFunc(interp, iclsPtr, namePtr, arglist,
body, imPtrPtr, 0);
}
/*
* ------------------------------------------------------------------------
* Itcl_ChangeMemberFunc()
*
* Modifies the data record representing a member function. This
* is usually the body of the function, but can include the argument
* list if it was not defined when the member was first created.
* If the body is of the form "@name", then it is treated as a label
* for a C procedure registered by Itcl_RegisterC().
*
* If any errors are encountered, this procedure returns TCL_ERROR
* along with an error message in the interpreter. Otherwise, it
* returns TCL_OK, and "imPtr" returns a pointer to the new
* member function.
* ------------------------------------------------------------------------
*/
int
Itcl_ChangeMemberFunc(
Tcl_Interp* interp, /* interpreter managing this action */
ItclMemberFunc* imPtr, /* command member being changed */
const char* arglist, /* space-separated list of arg names */
const char* body) /* body of commands for the method */
{
Tcl_HashEntry *hPtr;
ItclMemberCode *mcode = NULL;
int isNewEntry;
/*
* Try to create the implementation for this command member.
*/
if (ItclCreateMemberCode(interp, imPtr->iclsPtr,
arglist, body, &mcode, imPtr->namePtr, 0) != TCL_OK) {
return TCL_ERROR;
}
/*
* If the argument list was defined when the function was
* created, compare the arg lists or usage strings to make sure
* that the interface is not being redefined.
*/
if ((imPtr->flags & ITCL_ARG_SPEC) != 0 &&
(imPtr->argListPtr != NULL) &&
!EquivArgLists(interp, imPtr->argListPtr, mcode->argListPtr)) {
const char *argsStr;
if (imPtr->origArgsPtr != NULL) {
argsStr = Tcl_GetString(imPtr->origArgsPtr);
} else {
argsStr = "";
}
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"argument list changed for function \"",
Tcl_GetString(imPtr->fullNamePtr), "\": should be \"",
argsStr, "\"",
NULL);
Itcl_PreserveData(mcode);
Itcl_ReleaseData(mcode);
return TCL_ERROR;
}
if (imPtr->flags & ITCL_CONSTRUCTOR) {
/*
* REVISE mcode->bodyPtr here!
* Include a [my ItclConstructBase $iclsPtr] method call.
* Inherited from itcl::Root
*/
Tcl_Obj *newBody = Tcl_NewStringObj("", -1);
Tcl_AppendToObj(newBody,
"[::info object namespace ${this}]::my ItclConstructBase ", -1);
Tcl_AppendObjToObj(newBody, imPtr->iclsPtr->fullNamePtr);
Tcl_AppendToObj(newBody, "\n", -1);
Tcl_AppendObjToObj(newBody, mcode->bodyPtr);
Tcl_DecrRefCount(mcode->bodyPtr);
mcode->bodyPtr = newBody;
Tcl_IncrRefCount(mcode->bodyPtr);
}
/*
* Free up the old implementation and install the new one.
*/
Itcl_PreserveData(mcode);
Itcl_ReleaseData(imPtr->codePtr);
imPtr->codePtr = mcode;
if (mcode->flags & ITCL_IMPLEMENT_TCL) {
ClientData pmPtr;
imPtr->tmPtr = Itcl_NewProcClassMethod(interp,
imPtr->iclsPtr->clsPtr, ItclCheckCallMethod, ItclAfterCallMethod,
ItclProcErrorProc, imPtr, imPtr->namePtr, mcode->argumentPtr,
mcode->bodyPtr, &pmPtr);
hPtr = Tcl_CreateHashEntry(&imPtr->iclsPtr->infoPtr->procMethods,
(char *)imPtr->tmPtr, &isNewEntry);
if (isNewEntry) {
Tcl_SetHashValue(hPtr, imPtr);
}
}
ItclAddClassFunctionDictInfo(interp, imPtr->iclsPtr, imPtr);
return TCL_OK;
}
static const char * type_reserved_words [] = {
"type",
"self",
"selfns",
NULL
};
/*
* ------------------------------------------------------------------------
* ItclCreateMemberCode()
*
* Creates the data record representing the implementation behind a
* class member function. This includes the argument list and the body
* of the function. If the body is of the form "@name", then it is
* treated as a label for a C procedure registered by Itcl_RegisterC().
*
* The implementation is kept by the member function definition, and
* controlled by a preserve/release paradigm. That way, if it is in
* use while it is being redefined, it will stay around long enough
* to avoid a core dump.
*
* If any errors are encountered, this procedure returns TCL_ERROR
* along with an error message in the interpreter. Otherwise, it
* returns TCL_OK, and "mcodePtr" returns a pointer to the new
* implementation.
* ------------------------------------------------------------------------
*/
static int
ItclCreateMemberCode(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class containing this member */
const char* arglist, /* space-separated list of arg names */
const char* body, /* body of commands for the method */
ItclMemberCode** mcodePtr, /* returns: pointer to new implementation */
Tcl_Obj *namePtr,
int flags)
{
int argc;
int maxArgc;
Tcl_Obj *usagePtr;
ItclArgList *argListPtr;
ItclMemberCode *mcode;
const char **cPtrPtr;
int haveError;
/*
* Allocate some space to hold the implementation.
*/
mcode = (ItclMemberCode*)Itcl_Alloc(sizeof(ItclMemberCode));
Itcl_EventuallyFree(mcode, (Tcl_FreeProc *)FreeMemberCode);
if (arglist) {
if (ItclCreateArgList(interp, arglist, &argc, &maxArgc, &usagePtr,
&argListPtr, NULL, NULL) != TCL_OK) {
Itcl_PreserveData(mcode);
Itcl_ReleaseData(mcode);
return TCL_ERROR;
}
mcode->argcount = argc;
mcode->maxargcount = maxArgc;
mcode->argListPtr = argListPtr;
mcode->usagePtr = usagePtr;
Tcl_IncrRefCount(mcode->usagePtr);
mcode->argumentPtr = Tcl_NewStringObj((const char *)arglist, -1);
Tcl_IncrRefCount(mcode->argumentPtr);
if (iclsPtr->flags & (ITCL_TYPE|ITCL_WIDGETADAPTOR)) {
haveError = 0;
while (argListPtr != NULL) {
cPtrPtr = &type_reserved_words[0];
while (*cPtrPtr != NULL) {
if ((argListPtr->namePtr != NULL) &&
(strcmp(Tcl_GetString(argListPtr->namePtr),
*cPtrPtr) == 0)) {
haveError = 1;
}
if ((flags & ITCL_COMMON) != 0) {
if (! (iclsPtr->infoPtr->functionFlags &
ITCL_TYPE_METHOD)) {
haveError = 0;
}
}
if (haveError) {
const char *startStr = "method ";
if (iclsPtr->infoPtr->functionFlags &
ITCL_TYPE_METHOD) {
startStr = "typemethod ";
}
/* FIXME should use iclsPtr->infoPtr->functionFlags here */
if ((namePtr != NULL) &&
(strcmp(Tcl_GetString(namePtr),
"constructor") == 0)) {
startStr = "";
}
Tcl_AppendResult(interp, startStr,
namePtr == NULL ? "??" :
Tcl_GetString(namePtr),
"'s arglist may not contain \"",
*cPtrPtr, "\" explicitly", NULL);
Itcl_PreserveData(mcode);
Itcl_ReleaseData(mcode);
return TCL_ERROR;
}
cPtrPtr++;
}
argListPtr = argListPtr->nextPtr;
}
}
mcode->flags |= ITCL_ARG_SPEC;
} else {
argc = 0;
argListPtr = NULL;
}
if (body) {
mcode->bodyPtr = Tcl_NewStringObj((const char *)body, -1);
} else {
mcode->bodyPtr = Tcl_NewStringObj((const char *)"", -1);
mcode->flags |= ITCL_IMPLEMENT_NONE;
}
Tcl_IncrRefCount(mcode->bodyPtr);
/*
* If the body definition starts with '@', then treat the value
* as a symbolic name for a C procedure.
*/
if (body == NULL) {
/* No-op */
} else {
if (*body == '@') {
Tcl_CmdProc *argCmdProc;
Tcl_ObjCmdProc *objCmdProc;
ClientData cdata;
int isDone;
isDone = 0;
if (strcmp(body, "@itcl-builtin-cget") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-configure") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-isa") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-createhull") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-keepcomponentoption") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-ignorecomponentoption") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-renamecomponentoption") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-addoptioncomponent") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-ignoreoptioncomponent") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-renameoptioncomponent") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-setupcomponent") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-initoptions") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-mytypemethod") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-mymethod") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-myproc") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-mytypevar") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-myvar") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-itcl_hull") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-callinstance") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-getinstancevar") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-installhull") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-installcomponent") == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-destroy") == 0) {
isDone = 1;
}
if (strncmp(body, "@itcl-builtin-setget", 20) == 0) {
isDone = 1;
}
if (strcmp(body, "@itcl-builtin-classunknown") == 0) {
isDone = 1;
}
if (!isDone) {
if (!Itcl_FindC(interp, body+1, &argCmdProc, &objCmdProc,
&cdata)) {
Tcl_AppendResult(interp,
"no registered C procedure with name \"",
body+1, "\"", NULL);
Itcl_PreserveData(mcode);
Itcl_ReleaseData(mcode);
return TCL_ERROR;
}
/*
* WARNING! WARNING! WARNING!
* This is a pretty dangerous approach. What's done here is
* to copy over the proc + clientData implementation that
* happens to be in place at the moment the method is
* (re-)defined. This denies any freedom for the clientData
* to be changed dynamically or for the implementation to
* shift from OBJCMD to ARGCMD or vice versa, which the
* Itcl_Register(Obj)C routines explicitly permit. The whole
* system also lacks any scheme to unregister.
*/
if (objCmdProc != NULL) {
mcode->flags |= ITCL_IMPLEMENT_OBJCMD;
mcode->cfunc.objCmd = objCmdProc;
mcode->clientData = cdata;
} else {
if (argCmdProc != NULL) {
mcode->flags |= ITCL_IMPLEMENT_ARGCMD;
mcode->cfunc.argCmd = argCmdProc;
mcode->clientData = cdata;
}
}
} else {
mcode->flags |= ITCL_IMPLEMENT_TCL|ITCL_BUILTIN;
}
} else {
/*
* Otherwise, treat the body as a chunk of Tcl code.
*/
mcode->flags |= ITCL_IMPLEMENT_TCL;
}
}
*mcodePtr = mcode;
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* Itcl_CreateMemberCode()
*
* Creates the data record representing the implementation behind a
* class member function. This includes the argument list and the body
* of the function. If the body is of the form "@name", then it is
* treated as a label for a C procedure registered by Itcl_RegisterC().
*
* A member function definition holds a handle for the implementation, and
* uses Itcl_PreserveData and Itcl_ReleaseData to manage its interest in it.
*
* If any errors are encountered, this procedure returns TCL_ERROR
* along with an error message in the interpreter. Otherwise, it
* returns TCL_OK, and stores a pointer to the new implementation in
* "mcodePtr".
* ------------------------------------------------------------------------
*/
int
Itcl_CreateMemberCode(
Tcl_Interp* interp, /* interpreter managing this action */
ItclClass *iclsPtr, /* class containing this member */
const char* arglist, /* space-separated list of arg names */
const char* body, /* body of commands for the method */
ItclMemberCode** mcodePtr) /* returns: pointer to new implementation */
{
return ItclCreateMemberCode(interp, iclsPtr, arglist, body, mcodePtr,
NULL, 0);
}
/*
* ------------------------------------------------------------------------
* Itcl_DeleteMemberCode()
*
* Destroys all data associated with the given command implementation.
* Invoked automatically by ItclReleaseData() when the implementation
* is no longer being used.
* ------------------------------------------------------------------------
*/
void FreeMemberCode (
ItclMemberCode* mCodePtr)
{
if (mCodePtr == NULL) {
return;
}
if (mCodePtr->argListPtr != NULL) {
ItclDeleteArgList(mCodePtr->argListPtr);
}
if (mCodePtr->usagePtr != NULL) {
Tcl_DecrRefCount(mCodePtr->usagePtr);
}
if (mCodePtr->argumentPtr != NULL) {
Tcl_DecrRefCount(mCodePtr->argumentPtr);
}
if (mCodePtr->bodyPtr != NULL) {
Tcl_DecrRefCount(mCodePtr->bodyPtr);
}
Itcl_Free(mCodePtr);
}
void
Itcl_DeleteMemberCode(
void* cdata) /* pointer to member code definition */
{
Itcl_ReleaseData((ItclMemberCode *)cdata);
}
/*
* ------------------------------------------------------------------------
* Itcl_GetMemberCode()
*
* Makes sure that the implementation for an [incr Tcl] code body is
* ready to run. Note that a member function can be declared without
* being defined. The class definition may contain a declaration of
* the member function, but its body may be defined in a separate file.
* If an undefined function is encountered, this routine automatically
* attempts to autoload it. If the body is implemented via Tcl code,
* then it is compiled here as well.
*
* Returns TCL_ERROR (along with an error message in the interpreter)
* if an error is encountered, or if the implementation is not defined
* and cannot be autoloaded. Returns TCL_OK if implementation is
* ready to use.
* ------------------------------------------------------------------------
*/
int
Itcl_GetMemberCode(
Tcl_Interp* interp, /* interpreter managing this action */
ItclMemberFunc* imPtr) /* member containing code body */
{
int result;
ItclMemberCode *mcode = imPtr->codePtr;
assert(mcode != NULL);
/*
* If the implementation has not yet been defined, try to
* autoload it now.
*/
if (!Itcl_IsMemberCodeImplemented(mcode)) {
Tcl_DString buf;
Tcl_DStringInit(&buf);
Tcl_DStringAppend(&buf, "::auto_load ", -1);
Tcl_DStringAppend(&buf, Tcl_GetString(imPtr->fullNamePtr), -1);
result = Tcl_EvalEx(interp, Tcl_DStringValue(&buf), -1, 0);
Tcl_DStringFree(&buf);
if (result != TCL_OK) {
Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf(
"\n (while autoloading code for \"%s\")",
Tcl_GetString(imPtr->fullNamePtr)));
return result;
}
Tcl_ResetResult(interp); /* get rid of 1/0 status */
}
/*
* If the implementation is still not available, then
* autoloading must have failed.
*
* TRICKY NOTE: If code has been autoloaded, then the
* old mcode pointer is probably invalid. Go back to
* the member and look at the current code pointer again.
*/
mcode = imPtr->codePtr;
assert(mcode != NULL);
if (!Itcl_IsMemberCodeImplemented(mcode)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"member function \"", Tcl_GetString(imPtr->fullNamePtr),
"\" is not defined and cannot be autoloaded",
NULL);
return TCL_ERROR;
}
return TCL_OK;
}
static int
CallItclObjectCmd(
ClientData data[],
Tcl_Interp *interp,
int result)
{
Tcl_Object oPtr;
ItclMemberFunc *imPtr = (ItclMemberFunc *)data[0];
ItclObject *ioPtr = (ItclObject *)data[1];
int objc = PTR2INT(data[2]);
Tcl_Obj **objv = (Tcl_Obj **)data[3];
ItclShowArgs(1, "CallItclObjectCmd", objc, objv);
if (ioPtr != NULL) {
ioPtr->hadConstructorError = 0;
}
if (imPtr->flags & (ITCL_CONSTRUCTOR|ITCL_DESTRUCTOR)) {
oPtr = ioPtr->oPtr;
} else {
oPtr = NULL;
}
if (oPtr != NULL) {
result = ItclObjectCmd(imPtr, interp, oPtr, imPtr->iclsPtr->clsPtr,
objc, objv);
} else {
result = ItclObjectCmd(imPtr, interp, NULL, NULL, objc, objv);
}
if (result != TCL_OK) {
if (ioPtr != NULL && ioPtr->hadConstructorError == 0) {
/* we are in a constructor call and did not yet have an error */
/* -1 means we are not in a constructor */
ioPtr->hadConstructorError = 1;
}
}
return result;
}
/*
* ------------------------------------------------------------------------
* Itcl_EvalMemberCode()
*
* Used to execute an ItclMemberCode representation of a code
* fragment. This code may be a body of Tcl commands, or a C handler
* procedure.
*
* Executes the command with the given arguments (objc,objv) and
* returns an integer status code (TCL_OK/TCL_ERROR). Returns the
* result string or an error message in the interpreter.
* ------------------------------------------------------------------------
*/
int
Itcl_EvalMemberCode(
Tcl_Interp *interp, /* current interpreter */
ItclMemberFunc *imPtr, /* member func, or NULL (for error messages) */
ItclObject *contextIoPtr, /* object context, or NULL */
int objc, /* number of arguments */
Tcl_Obj *const objv[]) /* argument objects */
{
ItclMemberCode *mcode;
void *callbackPtr;
int result = TCL_OK;
int i;
ItclShowArgs(1, "Itcl_EvalMemberCode", objc, objv);
/*
* If this code does not have an implementation yet, then
* try to autoload one. Also, if this is Tcl code, make sure
* that it's compiled and ready to use.
*/
if (Itcl_GetMemberCode(interp, imPtr) != TCL_OK) {
return TCL_ERROR;
}
mcode = imPtr->codePtr;
/*
* Bump the reference count on this code, in case it is
* redefined or deleted during execution.
*/
Itcl_PreserveData(mcode);
if ((imPtr->flags & ITCL_DESTRUCTOR) && (contextIoPtr != NULL)) {
contextIoPtr->destructorHasBeenCalled = 1;
}
/*
* Execute the code body...
*/
if (((mcode->flags & ITCL_IMPLEMENT_OBJCMD) != 0) ||
((mcode->flags & ITCL_IMPLEMENT_ARGCMD) != 0)) {
if ((mcode->flags & ITCL_IMPLEMENT_OBJCMD) != 0) {
result = (*mcode->cfunc.objCmd)(mcode->clientData,
interp, objc, objv);
} else {
if ((mcode->flags & ITCL_IMPLEMENT_ARGCMD) != 0) {
char **argv;
argv = (char**)ckalloc( (unsigned)(objc*sizeof(char*)) );
for (i=0; i < objc; i++) {
argv[i] = Tcl_GetString(objv[i]);
}
result = (*mcode->cfunc.argCmd)(mcode->clientData,
interp, objc, (const char **)argv);
ckfree((char*)argv);
}
}
} else {
if ((mcode->flags & ITCL_IMPLEMENT_TCL) != 0) {
callbackPtr = Itcl_GetCurrentCallbackPtr(interp);
Tcl_NRAddCallback(interp, CallItclObjectCmd, imPtr, contextIoPtr,
INT2PTR(objc), (void *)objv);
result = Itcl_NRRunCallbacks(interp, callbackPtr);
}
}
Itcl_ReleaseData(mcode);
return result;
}
/*
* ------------------------------------------------------------------------
* ItclEquivArgLists()
*
* Compares two argument lists to see if they are equivalent. The
* first list is treated as a prototype, and the second list must
* match it. Argument names may be different, but they must match in
* meaning. If one argument is optional, the corresponding argument
* must also be optional. If the prototype list ends with the magic
* "args" argument, then it matches everything in the other list.
*
* Returns non-zero if the argument lists are equivalent.
* ------------------------------------------------------------------------
*/
static int
EquivArgLists(
TCL_UNUSED(Tcl_Interp*),
ItclArgList *origArgs,
ItclArgList *realArgs)
{
ItclArgList *currPtr;
char *argName;
for (currPtr=origArgs; currPtr != NULL; currPtr=currPtr->nextPtr) {
if ((realArgs != NULL) && (realArgs->namePtr == NULL)) {
if (currPtr->namePtr != NULL) {
if (strcmp(Tcl_GetString(currPtr->namePtr), "args") != 0) {
/* the definition has more arguments */
return 0;
}
}
}
if (realArgs == NULL) {
if (currPtr->defaultValuePtr != NULL) {
/* default args must be there ! */
return 0;
}
if (currPtr->namePtr != NULL) {
if (strcmp(Tcl_GetString(currPtr->namePtr), "args") != 0) {
/* the definition has more arguments */
return 0;
}
}
return 1;
}
if (currPtr->namePtr == NULL) {
/* no args defined */
if (realArgs->namePtr != NULL) {
return 0;
}
return 1;
}
argName = Tcl_GetString(currPtr->namePtr);
if (strcmp(argName, "args") == 0) {
if (currPtr->nextPtr == NULL) {
/* this is the last arument */
return 1;
}
}
if (currPtr->defaultValuePtr != NULL) {
if (realArgs->defaultValuePtr != NULL) {
/* default values must be the same */
if (strcmp(Tcl_GetString(currPtr->defaultValuePtr),
Tcl_GetString(realArgs->defaultValuePtr)) != 0) {
return 0;
}
}
}
realArgs = realArgs->nextPtr;
}
if ((currPtr == NULL) && (realArgs != NULL)) {
/* new definition has more args then the old one */
return 0;
}
return 1;
}
/*
* ------------------------------------------------------------------------
* Itcl_GetContext()
*
* Convenience routine for looking up the current object/class context.
* Useful in implementing methods/procs to see what class, and perhaps
* what object, is active.
*
* Returns TCL_OK if the current namespace is a class namespace.
* Also returns pointers to the class definition, and to object
* data if an object context is active. Returns TCL_ERROR (along
* with an error message in the interpreter) if a class namespace
* is not active.
* ------------------------------------------------------------------------
*/
void
Itcl_SetContext(
Tcl_Interp *interp,
ItclObject *ioPtr)
{
int isNew;
Itcl_Stack *stackPtr;
Tcl_CallFrame *framePtr = Itcl_GetUplevelCallFrame(interp, 0);
ItclObjectInfo *infoPtr = (ItclObjectInfo *)Tcl_GetAssocData(interp,
ITCL_INTERP_DATA, NULL);
Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(&infoPtr->frameContext,
(char *)framePtr, &isNew);
ItclCallContext *contextPtr
= (ItclCallContext *) ckalloc(sizeof(ItclCallContext));
memset(contextPtr, 0, sizeof(ItclCallContext));
contextPtr->ioPtr = ioPtr;
contextPtr->refCount = 1;
if (!isNew) {
Tcl_Panic("frame already has context?!");
}
stackPtr = (Itcl_Stack *) ckalloc(sizeof(Itcl_Stack));
Itcl_InitStack(stackPtr);
Tcl_SetHashValue(hPtr, stackPtr);
Itcl_PushStack(contextPtr, stackPtr);
}
void
Itcl_UnsetContext(
Tcl_Interp *interp)
{
Tcl_CallFrame *framePtr = Itcl_GetUplevelCallFrame(interp, 0);
ItclObjectInfo *infoPtr = (ItclObjectInfo *)Tcl_GetAssocData(interp,
ITCL_INTERP_DATA, NULL);
Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&infoPtr->frameContext,
(char *)framePtr);
Itcl_Stack *stackPtr = (Itcl_Stack *) Tcl_GetHashValue(hPtr);
ItclCallContext *contextPtr = (ItclCallContext *)Itcl_PopStack(stackPtr);
if (Itcl_GetStackSize(stackPtr) > 0) {
Tcl_Panic("frame context stack not empty!");
}
Itcl_DeleteStack(stackPtr);
ckfree((char *) stackPtr);
Tcl_DeleteHashEntry(hPtr);
if (contextPtr->refCount-- > 1) {
Tcl_Panic("frame context ref count not zero!");
}
ckfree((char *)contextPtr);
}
int
Itcl_GetContext(
Tcl_Interp *interp, /* current interpreter */
ItclClass **iclsPtrPtr, /* returns: class definition or NULL */
ItclObject **ioPtrPtr) /* returns: object data or NULL */
{
Tcl_Namespace *nsPtr;
/* Fetch the current call frame. That determines context. */
Tcl_CallFrame *framePtr = Itcl_GetUplevelCallFrame(interp, 0);
/* Try to map it to a context stack. */
ItclObjectInfo *infoPtr = (ItclObjectInfo *)Tcl_GetAssocData(interp,
ITCL_INTERP_DATA, NULL);
Tcl_HashEntry *hPtr = Tcl_FindHashEntry(&infoPtr->frameContext,
(char *)framePtr);
if (hPtr) {
/* Frame maps to a context stack. */
Itcl_Stack *stackPtr = (Itcl_Stack *)Tcl_GetHashValue(hPtr);
ItclCallContext *contextPtr = (ItclCallContext *)Itcl_PeekStack(stackPtr);
assert(contextPtr);
if (contextPtr->objectFlags & ITCL_OBJECT_ROOT_METHOD) {
ItclObject *ioPtr = contextPtr->ioPtr;
*iclsPtrPtr = ioPtr->iclsPtr;
*ioPtrPtr = ioPtr;
return TCL_OK;
}
*iclsPtrPtr = contextPtr->imPtr ? contextPtr->imPtr->iclsPtr
: contextPtr->ioPtr->iclsPtr;
*ioPtrPtr = contextPtr->ioPtr ? contextPtr->ioPtr : infoPtr->currIoPtr;
return TCL_OK;
}
/* Frame has no Itcl context data. No way to get object context. */
*ioPtrPtr = NULL;
/* Fall back to namespace for possible class context info. */
nsPtr = Tcl_GetCurrentNamespace(interp);
hPtr = Tcl_FindHashEntry(&infoPtr->namespaceClasses, (char *)nsPtr);
if (hPtr) {
*iclsPtrPtr = (ItclClass *)Tcl_GetHashValue(hPtr);
/*
* DANGER! Following stanza of code was added to address a
* regression from Itcl 4.0 -> Itcl 4.1 reported in Ticket
* [c949e73d3e] without really understanding. May be trouble here!
*/
if ((*iclsPtrPtr)->nsPtr) {
*ioPtrPtr = (*iclsPtrPtr)->infoPtr->currIoPtr;
}
return TCL_OK;
}
/* Cannot get any context. Record an error message. */
if (interp) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf(
"namespace \"%s\" is not a class namespace", nsPtr->fullName));
}
return TCL_ERROR;
}
/*
* ------------------------------------------------------------------------
* Itcl_GetMemberFuncUsage()
*
* Returns a string showing how a command member should be invoked.
* If the command member is a method, then the specified object name
* is reported as part of the invocation path:
*
* obj method arg ?arg arg ...?
*
* Otherwise, the "obj" pointer is ignored, and the class name is
* used as the invocation path:
*
* class::proc arg ?arg arg ...?
*
* Returns the string by appending it onto the Tcl_Obj passed in as
* an argument.
* ------------------------------------------------------------------------
*/
void
Itcl_GetMemberFuncUsage(
ItclMemberFunc *imPtr, /* command member being examined */
ItclObject *contextIoPtr, /* invoked with respect to this object */
Tcl_Obj *objPtr) /* returns: string showing usage */
{
Tcl_HashEntry *entry;
ItclMemberFunc *mf;
ItclClass *iclsPtr;
char *name;
char *arglist;
/*
* If the command is a method and an object context was
* specified, then add the object context. If the method
* was a constructor, and if the object is being created,
* then report the invocation via the class creation command.
*/
if ((imPtr->flags & ITCL_COMMON) == 0) {
if ((imPtr->flags & ITCL_CONSTRUCTOR) != 0 &&
contextIoPtr->constructed) {
iclsPtr = (ItclClass*)contextIoPtr->iclsPtr;
mf = NULL;
objPtr = Tcl_NewStringObj("constructor", -1);
entry = Tcl_FindHashEntry(&iclsPtr->resolveCmds, (char *)objPtr);
Tcl_DecrRefCount(objPtr);
if (entry) {
ItclCmdLookup *clookup;
clookup = (ItclCmdLookup *)Tcl_GetHashValue(entry);
mf = clookup->imPtr;
}
if (mf == imPtr) {
Tcl_GetCommandFullName(contextIoPtr->iclsPtr->interp,
contextIoPtr->iclsPtr->accessCmd, objPtr);
Tcl_AppendToObj(objPtr, " ", -1);
name = (char *) Tcl_GetCommandName(
contextIoPtr->iclsPtr->interp, contextIoPtr->accessCmd);
Tcl_AppendToObj(objPtr, name, -1);
} else {
Tcl_AppendToObj(objPtr, Tcl_GetString(imPtr->fullNamePtr), -1);
}
} else {
if (contextIoPtr && contextIoPtr->accessCmd) {
name = (char *) Tcl_GetCommandName(
contextIoPtr->iclsPtr->interp, contextIoPtr->accessCmd);
Tcl_AppendStringsToObj(objPtr, name, " ",
Tcl_GetString(imPtr->namePtr), NULL);
} else {
Tcl_AppendStringsToObj(objPtr, "<object> ",
Tcl_GetString(imPtr->namePtr), NULL);
}
}
} else {
Tcl_AppendToObj(objPtr, Tcl_GetString(imPtr->fullNamePtr), -1);
}
/*
* Add the argument usage info.
*/
if (imPtr->codePtr) {
if (imPtr->codePtr->usagePtr != NULL) {
arglist = Tcl_GetString(imPtr->codePtr->usagePtr);
} else {
arglist = NULL;
}
} else {
if (imPtr->argListPtr != NULL) {
arglist = Tcl_GetString(imPtr->usagePtr);
} else {
arglist = NULL;
}
}
if (arglist) {
if (strlen(arglist) > 0) {
Tcl_AppendToObj(objPtr, " ", -1);
Tcl_AppendToObj(objPtr, arglist, -1);
}
}
}
/*
* ------------------------------------------------------------------------
* Itcl_ExecMethod()
*
* Invoked by Tcl to handle the execution of a user-defined method.
* A method is similar to the usual Tcl proc, but has access to
* object-specific data. If for some reason there is no current
* object context, then a method call is inappropriate, and an error
* is returned.
*
* Methods are implemented either as Tcl code fragments, or as C-coded
* procedures. For Tcl code fragments, command arguments are parsed
* according to the argument list, and the body is executed in the
* scope of the class where it was defined. For C procedures, the
* arguments are passed in "as-is", and the procedure is executed in
* the most-specific class scope.
* ------------------------------------------------------------------------
*/
static int
NRExecMethod(
ClientData clientData, /* method definition */
Tcl_Interp *interp, /* current interpreter */
int objc, /* number of arguments */
Tcl_Obj *const *objv) /* argument objects */
{
ItclMemberFunc *imPtr = (ItclMemberFunc*)clientData;
int result = TCL_OK;
const char *token;
Tcl_HashEntry *entry;
ItclClass *iclsPtr;
ItclObject *ioPtr;
ItclShowArgs(1, "NRExecMethod", objc, objv);
/*
* Make sure that the current namespace context includes an
* object that is being manipulated. Methods can be executed
* only if an object context exists.
*/
iclsPtr = imPtr->iclsPtr;
if (Itcl_GetContext(interp, &iclsPtr, &ioPtr) != TCL_OK) {
return TCL_ERROR;
}
if (ioPtr == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"cannot access object-specific info without an object context",
NULL);
return TCL_ERROR;
}
/*
* Make sure that this command member can be accessed from
* the current namespace context.
* That is now done in ItclMapMethodNameProc !!
*/
/*
* All methods should be "virtual" unless they are invoked with
* a "::" scope qualifier.
*
* To implement the "virtual" behavior, find the most-specific
* implementation for the method by looking in the "resolveCmds"
* table for this class.
*/
token = Tcl_GetString(objv[0]);
if (strstr(token, "::") == NULL) {
if (ioPtr != NULL) {
entry = Tcl_FindHashEntry(&ioPtr->iclsPtr->resolveCmds,
(char *)imPtr->namePtr);
if (entry) {
ItclCmdLookup *clookup;
clookup = (ItclCmdLookup *)Tcl_GetHashValue(entry);
imPtr = clookup->imPtr;
}
}
}
/*
* Execute the code for the method. Be careful to protect
* the method in case it gets deleted during execution.
*/
Itcl_PreserveData(imPtr);
result = Itcl_EvalMemberCode(interp, imPtr, ioPtr, objc, objv);
Itcl_ReleaseData(imPtr);
return result;
}
/* ARGSUSED */
int
Itcl_ExecMethod(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const *objv)
{
return Tcl_NRCallObjProc(interp, NRExecMethod, clientData, objc, objv);
}
/*
* ------------------------------------------------------------------------
* Itcl_ExecProc()
*
* Invoked by Tcl to handle the execution of a user-defined proc.
*
* Procs are implemented either as Tcl code fragments, or as C-coded
* procedures. For Tcl code fragments, command arguments are parsed
* according to the argument list, and the body is executed in the
* scope of the class where it was defined. For C procedures, the
* arguments are passed in "as-is", and the procedure is executed in
* the most-specific class scope.
* ------------------------------------------------------------------------
*/
static int
NRExecProc(
ClientData clientData, /* proc definition */
Tcl_Interp *interp, /* current interpreter */
int objc, /* number of arguments */
Tcl_Obj *const objv[]) /* argument objects */
{
ItclMemberFunc *imPtr = (ItclMemberFunc*)clientData;
int result = TCL_OK;
ItclShowArgs(1, "NRExecProc", objc, objv);
/*
* Make sure that this command member can be accessed from
* the current namespace context.
*/
if (imPtr->protection != ITCL_PUBLIC) {
if (!Itcl_CanAccessFunc(imPtr, Tcl_GetCurrentNamespace(interp))) {
ItclMemberFunc *imPtr2 = NULL;
Tcl_HashEntry *hPtr;
Tcl_ObjectContext context;
context = (Tcl_ObjectContext)Itcl_GetCallFrameClientData(interp);
if (context == NULL) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"can't access \"", Tcl_GetString(imPtr->fullNamePtr),
"\": ", Itcl_ProtectionStr(imPtr->protection),
" function", NULL);
return TCL_ERROR;
}
hPtr = Tcl_FindHashEntry(&imPtr->iclsPtr->infoPtr->procMethods,
(char *)Tcl_ObjectContextMethod(context));
if (hPtr != NULL) {
imPtr2 = (ItclMemberFunc *)Tcl_GetHashValue(hPtr);
}
if ((imPtr->protection & ITCL_PRIVATE) && (imPtr2 != NULL) &&
(imPtr->iclsPtr->nsPtr != imPtr2->iclsPtr->nsPtr)) {
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"invalid command name \"",
Tcl_GetString(objv[0]),
"\"", NULL);
return TCL_ERROR;
}
Tcl_AppendStringsToObj(Tcl_GetObjResult(interp),
"can't access \"", Tcl_GetString(imPtr->fullNamePtr),
"\": ", Itcl_ProtectionStr(imPtr->protection),
" function", NULL);
return TCL_ERROR;
}
}
/*
* Execute the code for the proc. Be careful to protect
* the proc in case it gets deleted during execution.
*/
Itcl_PreserveData(imPtr);
result = Itcl_EvalMemberCode(interp, imPtr, NULL,
objc, objv);
Itcl_ReleaseData(imPtr);
return result;
}
/* ARGSUSED */
int
Itcl_ExecProc(
ClientData clientData,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const *objv)
{
return Tcl_NRCallObjProc(interp, NRExecProc, clientData, objc, objv);
}
static int
CallInvokeMethodIfExists(
ClientData data[],
Tcl_Interp *interp,
int result)
{
ItclClass *iclsPtr = (ItclClass *)data[0];
ItclObject *contextObj = (ItclObject *)data[1];
int objc = PTR2INT(data[2]);
Tcl_Obj *const *objv = (Tcl_Obj *const *)data[3];
result = Itcl_InvokeMethodIfExists(interp, "constructor",
iclsPtr, contextObj, objc, (Tcl_Obj* const*)objv);
if (result != TCL_OK) {
return TCL_ERROR;
}
return TCL_OK;
}
/*
* ------------------------------------------------------------------------
* Itcl_ConstructBase()
*
* Usually invoked just before executing the body of a constructor
* when an object is first created. This procedure makes sure that
* all base classes are properly constructed. If an "initCode" fragment
* was defined with the constructor for the class, then it is invoked.
* After that, the list of base classes is checked for constructors
* that are defined but have not yet been invoked. Each of these is
* invoked implicitly with no arguments.
*
* Assumes that a local call frame is already installed, and that
* constructor arguments have already been matched and are sitting in
* this frame. Returns TCL_OK on success; otherwise, this procedure
* returns TCL_ERROR, along with an error message in the interpreter.
* ------------------------------------------------------------------------
*/
int
Itcl_ConstructBase(
Tcl_Interp *interp, /* interpreter */
ItclObject *contextObj, /* object being constructed */
ItclClass *contextClass) /* current class being constructed */
{
int result = TCL_OK;
Tcl_Obj *objPtr;
Itcl_ListElem *elem;
/*
* If the class has an "initCode", invoke it in the current context.
*/
if (contextClass->initCode) {
/* TODO: NRE */
result = Tcl_EvalObjEx(interp, contextClass->initCode, 0);
}
/*
* Scan through the list of base classes and see if any of these
* have not been constructed. Invoke base class constructors
* implicitly, as needed. Go through the list of base classes
* in reverse order, so that least-specific classes are constructed
* first.
*/
objPtr = Tcl_NewStringObj("constructor", -1);
Tcl_IncrRefCount(objPtr);
for (elem = Itcl_LastListElem(&contextClass->bases);
result == TCL_OK && elem != NULL;
elem = Itcl_PrevListElem(elem)) {
Tcl_HashEntry *entry;
ItclClass *iclsPtr = (ItclClass*)Itcl_GetListValue(elem);
if (Tcl_FindHashEntry(contextObj->constructed,
(char *)iclsPtr->namePtr)) {
/* Already constructed, nothing to do. */
continue;
}
entry = Tcl_FindHashEntry(&iclsPtr->functions, (char *)objPtr);
if (entry) {
void *callbackPtr = Itcl_GetCurrentCallbackPtr(interp);
Tcl_NRAddCallback(interp, CallInvokeMethodIfExists, iclsPtr,
contextObj, INT2PTR(0), NULL);
result = Itcl_NRRunCallbacks(interp, callbackPtr);
} else {
result = Itcl_ConstructBase(interp, contextObj, iclsPtr);
}
}
Tcl_DecrRefCount(objPtr);
return result;
}
int
ItclConstructGuts(
ItclObject *contextObj,
Tcl_Interp *interp,
int objc,
Tcl_Obj *const objv[])
{
ItclClass *contextClass;
/* Ignore syntax error */
if (objc != 3) {
return TCL_OK;
}
/* Object is fully constructed. This becomes no-op. */
if (contextObj->constructed == NULL) {
return TCL_OK;
}
contextClass = Itcl_FindClass(interp, Tcl_GetString(objv[2]), 0);
if (contextClass == NULL) {
return TCL_OK;
}
return Itcl_ConstructBase(interp, contextObj, contextClass);
}
/*
* ------------------------------------------------------------------------
* Itcl_InvokeMethodIfExists()
*
* Looks for a particular method in the specified class. If the
* method is found, it is invoked with the given arguments. Any
* protection level (protected/private) for the method is ignored.
* If the method does not exist, this procedure does nothing.
*
* This procedure is used primarily to invoke the constructor/destructor
* when an object is created/destroyed.
*
* Returns TCL_OK on success; otherwise, this procedure returns
* TCL_ERROR along with an error message in the interpreter.
* ------------------------------------------------------------------------
*/
int
Itcl_InvokeMethodIfExists(
Tcl_Interp *interp, /* interpreter */
const char *name, /* name of desired method */
ItclClass *contextClassPtr, /* current class being constructed */
ItclObject *contextObjectPtr, /* object being constructed */
int objc, /* number of arguments */
Tcl_Obj *const objv[]) /* argument objects */
{
Tcl_HashEntry *hPtr;
Tcl_Obj *cmdlinePtr;
Tcl_Obj **cmdlinev;
Tcl_Obj **newObjv;
Tcl_CallFrame frame;
ItclMemberFunc *imPtr;
int cmdlinec;
int result = TCL_OK;
Tcl_Obj *objPtr = Tcl_NewStringObj(name, -1);
ItclShowArgs(1, "Itcl_InvokeMethodIfExists", objc, objv);
hPtr = Tcl_FindHashEntry(&contextClassPtr->functions, (char *)objPtr);
Tcl_DecrRefCount(objPtr);
if (hPtr) {
imPtr = (ItclMemberFunc*)Tcl_GetHashValue(hPtr);
/*
* Prepend the method name to the list of arguments.
*/
cmdlinePtr = Itcl_CreateArgs(interp, name, objc, objv);
(void) Tcl_ListObjGetElements(NULL, cmdlinePtr,
&cmdlinec, &cmdlinev);
ItclShowArgs(1, "EMC", cmdlinec, cmdlinev);
/*
* Execute the code for the method. Be careful to protect
* the method in case it gets deleted during execution.
*/
Itcl_PreserveData(imPtr);
if (contextObjectPtr->oPtr == NULL) {
Tcl_DecrRefCount(cmdlinePtr);
return TCL_ERROR;
}
result = Itcl_EvalMemberCode(interp, imPtr, contextObjectPtr,
cmdlinec, cmdlinev);
Itcl_ReleaseData(imPtr);
Tcl_DecrRefCount(cmdlinePtr);
} else {
if (contextClassPtr->flags &
(ITCL_ECLASS|ITCL_TYPE|ITCL_WIDGET|ITCL_WIDGETADAPTOR)) {
if (strcmp(name, "constructor") == 0) {
if (objc > 0) {
if (contextClassPtr->numOptions == 0) {
/* check if all options are delegeted */
Tcl_Obj *objPtr;
objPtr = Tcl_NewStringObj("*", -1);
hPtr = Tcl_FindHashEntry(
&contextClassPtr->delegatedOptions,
(char *)objPtr);
Tcl_DecrRefCount(objPtr);
if (hPtr == NULL) {
Tcl_AppendResult(interp, "type \"",
Tcl_GetString(contextClassPtr->namePtr),
"\" has no options, but constructor has",
" option arguments", NULL);
return TCL_ERROR;
}
}
if (Itcl_PushCallFrame(interp, &frame,
contextClassPtr->nsPtr,
/*isProcCallFrame*/0) != TCL_OK) {
Tcl_AppendResult(interp, "INTERNAL ERROR in",
"Itcl_InvokeMethodIfExists Itcl_PushCallFrame",
NULL);
}
newObjv = (Tcl_Obj **)ckalloc(sizeof(Tcl_Obj *)*(objc + 2));
newObjv[0] = Tcl_NewStringObj("my", -1);
Tcl_IncrRefCount(newObjv[0]);
newObjv[1] = Tcl_NewStringObj("configure", -1);
Tcl_IncrRefCount(newObjv[1]);
memcpy(newObjv + 2, objv, (objc * sizeof(Tcl_Obj *)));
ItclShowArgs(1, "DEFAULT Constructor", objc + 2, newObjv);
result = Tcl_EvalObjv(interp, objc + 2, newObjv, 0);
Tcl_DecrRefCount(newObjv[1]);
Tcl_DecrRefCount(newObjv[0]);
ckfree((char *)newObjv);
Itcl_PopCallFrame(interp);
}
}
}
}
return result;
}
/*
* ------------------------------------------------------------------------
* Itcl_ReportFuncErrors()
*
* Used to interpret the status code returned when the body of a
* Tcl-style proc is executed. Handles the "errorInfo" and "errorCode"
* variables properly, and adds error information into the interpreter
* if anything went wrong. Returns a new status code that should be
* treated as the return status code for the command.
*
* This same operation is usually buried in the Tcl InterpProc()
* procedure. It is defined here so that it can be reused more easily.
* ------------------------------------------------------------------------
*/
int
Itcl_ReportFuncErrors(
TCL_UNUSED(Tcl_Interp*), /* interpreter being modified */
TCL_UNUSED(ItclMemberFunc*), /* command member that was invoked */
TCL_UNUSED(ItclObject*), /* object context for this command */
int result) /* integer status code from proc body */
{
/* FIXME !!! */
/* adapt to use of ItclProcErrorProc for stubs compatibility !! */
return result;
}
/*
* ------------------------------------------------------------------------
* Itcl_CmdAliasProc()
*
* ------------------------------------------------------------------------
*/
Tcl_Command
Itcl_CmdAliasProc(
Tcl_Interp *interp,
Tcl_Namespace *nsPtr,
const char *cmdName,
ClientData clientData)
{
Tcl_HashEntry *hPtr;
Tcl_Obj *objPtr;
ItclObjectInfo *infoPtr;
ItclClass *iclsPtr;
ItclObject *ioPtr;
ItclMemberFunc *imPtr;
ItclResolveInfo *resolveInfoPtr;
ItclCmdLookup *clookup;
resolveInfoPtr = (ItclResolveInfo *)clientData;
if (resolveInfoPtr->flags & ITCL_RESOLVE_OBJECT) {
ioPtr = resolveInfoPtr->ioPtr;
iclsPtr = ioPtr->iclsPtr;
} else {
ioPtr = NULL;
iclsPtr = resolveInfoPtr->iclsPtr;
}
infoPtr = iclsPtr->infoPtr;
hPtr = Tcl_FindHashEntry(&infoPtr->namespaceClasses, (char *)nsPtr);
if (hPtr == NULL) {
return NULL;
}
iclsPtr = (ItclClass *)Tcl_GetHashValue(hPtr);
objPtr = Tcl_NewStringObj(cmdName, -1);
hPtr = Tcl_FindHashEntry(&iclsPtr->resolveCmds, (char *)objPtr);
Tcl_DecrRefCount(objPtr);
if (hPtr == NULL) {
if (strcmp(cmdName, "@itcl-builtin-cget") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::cget", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-configure") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::configure", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-destroy") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::destroy", NULL, 0);
}
if (strncmp(cmdName, "@itcl-builtin-setget", 20) == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::setget", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-isa") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::isa", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-createhull") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::createhull", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-keepcomponentoption") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::keepcomponentoption", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-ignorecomponentoption") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::removecomponentoption", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-irgnorecomponentoption") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::ignorecomponentoption", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-setupcomponent") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::setupcomponent", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-initoptions") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::initoptions", NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-mytypemethod") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::mytypemethod",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-mymethod") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::mymethod",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-myproc") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::myproc",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-mytypevar") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::mytypevar",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-myvar") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::myvar",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-itcl_hull") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::itcl_hull",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-callinstance") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::callinstance",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-getinstancevar") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::getinstancevar",
NULL, 0);
}
if (strcmp(cmdName, "@itcl-builtin-classunknown") == 0) {
return Tcl_FindCommand(interp, "::itcl::builtin::classunknown", NULL, 0);
}
return NULL;
}
clookup = (ItclCmdLookup *)Tcl_GetHashValue(hPtr);
imPtr = clookup->imPtr;
return imPtr->accessCmd;
}
/*
* ------------------------------------------------------------------------
* Itcl_VarAliasProc()
*
* ------------------------------------------------------------------------
*/
Tcl_Var
Itcl_VarAliasProc(
TCL_UNUSED(Tcl_Interp*),
Tcl_Namespace *nsPtr,
const char *varName,
ClientData clientData)
{
Tcl_HashEntry *hPtr;
ItclObjectInfo *infoPtr;
ItclClass *iclsPtr;
ItclObject *ioPtr;
ItclVarLookup *ivlPtr;
ItclResolveInfo *resolveInfoPtr;
ItclCallContext *callContextPtr;
Tcl_Var varPtr;
varPtr = NULL;
hPtr = NULL;
callContextPtr = NULL;
resolveInfoPtr = (ItclResolveInfo *)clientData;
if (resolveInfoPtr->flags & ITCL_RESOLVE_OBJECT) {
ioPtr = resolveInfoPtr->ioPtr;
iclsPtr = ioPtr->iclsPtr;
} else {
ioPtr = NULL;
iclsPtr = resolveInfoPtr->iclsPtr;
}
infoPtr = iclsPtr->infoPtr;
hPtr = Tcl_FindHashEntry(&infoPtr->namespaceClasses, (char *)nsPtr);
if (hPtr != NULL) {
iclsPtr = (ItclClass *)Tcl_GetHashValue(hPtr);
}
hPtr = ItclResolveVarEntry(iclsPtr, varName);
if (hPtr == NULL) {
/* no class/object variable */
return NULL;
}
ivlPtr = (ItclVarLookup *)Tcl_GetHashValue(hPtr);
if (ivlPtr == NULL) {
return NULL;
}
if (!ivlPtr->accessible) {
return NULL;
}
if (ioPtr != NULL) {
hPtr = Tcl_FindHashEntry(&ioPtr->objectVariables,
(char *)ivlPtr->ivPtr);
} else {
hPtr = Tcl_FindHashEntry(&iclsPtr->classCommons,
(char *)ivlPtr->ivPtr);
if (hPtr == NULL) {
if (callContextPtr != NULL) {
ioPtr = callContextPtr->ioPtr;
}
if (ioPtr != NULL) {
hPtr = Tcl_FindHashEntry(&ioPtr->objectVariables,
(char *)ivlPtr->ivPtr);
}
}
}
if (hPtr != NULL) {
varPtr = (Tcl_Var)Tcl_GetHashValue(hPtr);
}
return varPtr;
}
/*
* ------------------------------------------------------------------------
* ItclCheckCallProc()
*
*
* ------------------------------------------------------------------------
*/
int
ItclCheckCallProc(
ClientData clientData,
Tcl_Interp *interp,
TCL_UNUSED(Tcl_ObjectContext),
TCL_UNUSED(Tcl_CallFrame*),
int *isFinished)
{
int result;
ItclMemberFunc *imPtr;
imPtr = (ItclMemberFunc *)clientData;
if (!imPtr->iclsPtr->infoPtr->useOldResolvers) {
Itcl_SetCallFrameResolver(interp, imPtr->iclsPtr->resolvePtr);
}
result = TCL_OK;
if (isFinished != NULL) {
*isFinished = 0;
}
return result;
}
/*
* ------------------------------------------------------------------------
* ItclCheckCallMethod()
*
*
* ------------------------------------------------------------------------
*/
int
ItclCheckCallMethod(
ClientData clientData,
Tcl_Interp *interp,
Tcl_ObjectContext contextPtr,
Tcl_CallFrame *framePtr,
int *isFinished)
{
Itcl_Stack *stackPtr;
Tcl_Object oPtr;
ItclObject *ioPtr;
Tcl_HashEntry *hPtr;
Tcl_Obj *const * cObjv;
Tcl_Namespace *currNsPtr;
ItclCallContext *callContextPtr;
ItclCallContext *callContextPtr2;
ItclMemberFunc *imPtr;
int result;
int isNew;
int cObjc;
int min_allowed_args;
ItclObjectInfo *infoPtr;
oPtr = NULL;
hPtr = NULL;
imPtr = (ItclMemberFunc *)clientData;
Itcl_PreserveData(imPtr);
if (imPtr->flags & ITCL_CONSTRUCTOR) {
ioPtr = imPtr->iclsPtr->infoPtr->currIoPtr;
} else {
if (contextPtr == NULL) {
if ((imPtr->flags & ITCL_COMMON) ||
(imPtr->codePtr->flags & ITCL_BUILTIN)) {
if (!imPtr->iclsPtr->infoPtr->useOldResolvers) {
Itcl_SetCallFrameResolver(interp,
imPtr->iclsPtr->resolvePtr);
}
if (isFinished != NULL) {
*isFinished = 0;
}
return TCL_OK;
}
Tcl_AppendResult(interp,
"ItclCheckCallMethod cannot get context object (NULL)",
" for ", Tcl_GetString(imPtr->fullNamePtr),
NULL);
result = TCL_ERROR;
goto finishReturn;
}
oPtr = Tcl_ObjectContextObject(contextPtr);
ioPtr = (ItclObject *)Tcl_ObjectGetMetadata(oPtr,
imPtr->iclsPtr->infoPtr->object_meta_type);
}
if ((imPtr->codePtr != NULL) &&
(imPtr->codePtr->flags & ITCL_IMPLEMENT_NONE)) {
Tcl_AppendResult(interp, "member function \"",
Tcl_GetString(imPtr->fullNamePtr),
"\" is not defined and cannot be autoloaded", NULL);
if (isFinished != NULL) {
*isFinished = 1;
}
result = TCL_ERROR;
goto finishReturn;
}
if (framePtr) {
/*
* This stanza is in place to seize control over usage error messages
* before TclOO examines the arguments and produces its own. This
* gives Itcl stability in its error messages at the cost of inconsistency
* with Tcl's evolving conventions.
*/
cObjc = Itcl_GetCallFrameObjc(interp);
cObjv = Itcl_GetCallFrameObjv(interp);
min_allowed_args = cObjc-2;
if (strcmp(Tcl_GetString(cObjv[0]), "next") == 0) {
min_allowed_args++;
}
if (min_allowed_args < imPtr->argcount) {
Tcl_AppendResult(interp, "wrong # args: should be \"",
Tcl_GetString(cObjv[0]), " ", Tcl_GetString(imPtr->namePtr),
" ", Tcl_GetString(imPtr->usagePtr), "\"", NULL);
if (isFinished != NULL) {
*isFinished = 1;
}
result = TCL_ERROR;
goto finishReturn;
}
}
isNew = 0;
callContextPtr = NULL;
currNsPtr = Tcl_GetCurrentNamespace(interp);
if (ioPtr != NULL) {
hPtr = Tcl_CreateHashEntry(&ioPtr->contextCache, (char *)imPtr, &isNew);
if (!isNew) {
callContextPtr2 = (ItclCallContext *)Tcl_GetHashValue(hPtr);
if (callContextPtr2->refCount == 0) {
callContextPtr = callContextPtr2;
callContextPtr->objectFlags = ioPtr->flags;
callContextPtr->nsPtr = Tcl_GetCurrentNamespace(interp);
callContextPtr->ioPtr = ioPtr;
callContextPtr->imPtr = imPtr;
callContextPtr->refCount = 1;
} else {
if ((callContextPtr2->objectFlags == ioPtr->flags)
&& (callContextPtr2->nsPtr == currNsPtr)) {
callContextPtr = callContextPtr2;
callContextPtr->refCount++;
}
}
}
}
if (callContextPtr == NULL) {
callContextPtr = (ItclCallContext *)ckalloc(
sizeof(ItclCallContext));
if (ioPtr == NULL) {
callContextPtr->objectFlags = 0;
callContextPtr->ioPtr = NULL;
} else {
callContextPtr->objectFlags = ioPtr->flags;
callContextPtr->ioPtr = ioPtr;
}
callContextPtr->nsPtr = Tcl_GetCurrentNamespace(interp);
callContextPtr->imPtr = imPtr;
callContextPtr->refCount = 1;
}
if (isNew) {
Tcl_SetHashValue(hPtr, callContextPtr);
}
if (framePtr == NULL) {
framePtr = Itcl_GetUplevelCallFrame(interp, 0);
}
isNew = 0;
infoPtr = imPtr->iclsPtr->infoPtr;
hPtr = Tcl_CreateHashEntry(&infoPtr->frameContext,
(char *)framePtr, &isNew);
if (isNew) {
stackPtr = (Itcl_Stack *)ckalloc(sizeof(Itcl_Stack));
Itcl_InitStack(stackPtr);
Tcl_SetHashValue(hPtr, stackPtr);
} else {
stackPtr = (Itcl_Stack *)Tcl_GetHashValue(hPtr);
}
assert (callContextPtr) ;
Itcl_PushStack(callContextPtr, stackPtr);
/* Ugly abuse alert. Two maps in one table */
hPtr = Tcl_CreateHashEntry(&infoPtr->frameContext,
(char *)contextPtr, &isNew);
if (isNew) {
stackPtr = (Itcl_Stack *)ckalloc(sizeof(Itcl_Stack));
Itcl_InitStack(stackPtr);
Tcl_SetHashValue(hPtr, stackPtr);
} else {
stackPtr = (Itcl_Stack *)Tcl_GetHashValue(hPtr);
}
Itcl_PushStack(framePtr, stackPtr);
if (ioPtr != NULL) {
ioPtr->callRefCount++;
Itcl_PreserveData(ioPtr); /* ++ preserve until ItclAfterCallMethod releases it */
}
imPtr->iclsPtr->callRefCount++;
if (!imPtr->iclsPtr->infoPtr->useOldResolvers) {
Itcl_SetCallFrameResolver(interp, ioPtr->resolvePtr);
}
result = TCL_OK;
if (isFinished != NULL) {
*isFinished = 0;
}
return result;
finishReturn:
Itcl_ReleaseData(imPtr);
return result;
}
/*
* ------------------------------------------------------------------------
* ItclAfterCallMethod()
*
*
* ------------------------------------------------------------------------
*/
int
ItclAfterCallMethod(
ClientData clientData,
Tcl_Interp *interp,
Tcl_ObjectContext contextPtr,
TCL_UNUSED(Tcl_Namespace*),
int call_result)
{
Tcl_HashEntry *hPtr;
ItclObject *ioPtr;
ItclMemberFunc *imPtr;
ItclCallContext *callContextPtr;
int newEntry;
int result;
imPtr = (ItclMemberFunc *)clientData;
callContextPtr = NULL;
if (contextPtr != NULL) {
ItclObjectInfo *infoPtr = imPtr->infoPtr;
Tcl_CallFrame *framePtr;
Itcl_Stack *stackPtr;
hPtr = Tcl_FindHashEntry(&infoPtr->frameContext, (char *)contextPtr);
assert(hPtr);
stackPtr = (Itcl_Stack *)Tcl_GetHashValue(hPtr);
framePtr = (Tcl_CallFrame *)Itcl_PopStack(stackPtr);
if (Itcl_GetStackSize(stackPtr) == 0) {
Itcl_DeleteStack(stackPtr);
ckfree((char *) stackPtr);
Tcl_DeleteHashEntry(hPtr);
}
hPtr = Tcl_FindHashEntry(&infoPtr->frameContext, (char *)framePtr);
assert(hPtr);
stackPtr = (Itcl_Stack *)Tcl_GetHashValue(hPtr);
callContextPtr = (ItclCallContext *)Itcl_PopStack(stackPtr);
if (Itcl_GetStackSize(stackPtr) == 0) {
Itcl_DeleteStack(stackPtr);
ckfree((char *) stackPtr);
Tcl_DeleteHashEntry(hPtr);
}
}
if (callContextPtr == NULL) {
if ((imPtr->flags & ITCL_COMMON) ||
(imPtr->codePtr->flags & ITCL_BUILTIN)) {
result = call_result;
goto finishReturn;
}
Tcl_AppendResult(interp,
"ItclAfterCallMethod cannot get context object (NULL)",
" for ", Tcl_GetString(imPtr->fullNamePtr), NULL);
result = TCL_ERROR;
goto finishReturn;
}
/*
* If this is a constructor or destructor, and if it is being
* invoked at the appropriate time, keep track of which methods
* have been called. This information is used to implicitly
* invoke constructors/destructors as needed.
*/
ioPtr = callContextPtr->ioPtr;
if (ioPtr != NULL) {
if (imPtr->iclsPtr) {
imPtr->iclsPtr->callRefCount--;
if (imPtr->flags & (ITCL_CONSTRUCTOR | ITCL_DESTRUCTOR)) {
if ((imPtr->flags & ITCL_DESTRUCTOR) && ioPtr &&
ioPtr->destructed) {
Tcl_CreateHashEntry(ioPtr->destructed,
(char *)imPtr->iclsPtr->namePtr, &newEntry);
}
if ((imPtr->flags & ITCL_CONSTRUCTOR) && ioPtr &&
ioPtr->constructed) {
Tcl_CreateHashEntry(ioPtr->constructed,
(char *)imPtr->iclsPtr->namePtr, &newEntry);
}
}
}
ioPtr->callRefCount--;
if (ioPtr->flags & ITCL_OBJECT_SHOULD_VARNS_DELETE) {
ItclDeleteObjectVariablesNamespace(interp, ioPtr);
}
}
if (callContextPtr->refCount-- <= 1) {
if (callContextPtr->ioPtr != NULL) {
hPtr = Tcl_FindHashEntry(&callContextPtr->ioPtr->contextCache,
(char *)callContextPtr->imPtr);
if (hPtr == NULL) {
ckfree((char *)callContextPtr);
}
} else {
ckfree((char *)callContextPtr);
}
}
if (ioPtr != NULL) {
Itcl_ReleaseData(ioPtr); /* -- paired release for preserve in ItclCheckCallMethod */
}
result = call_result;
finishReturn:
Itcl_ReleaseData(imPtr);
return result;
}
void
ItclProcErrorProc(
Tcl_Interp *interp,
TCL_UNUSED(Tcl_Obj*))
{
Tcl_Obj *objPtr;
Tcl_HashEntry *hPtr;
ItclObjectInfo *infoPtr;
ItclCallContext *callContextPtr;
ItclMemberFunc *imPtr;
ItclObject *contextIoPtr;
ItclClass *currIclsPtr;
char num[20];
Itcl_Stack *stackPtr;
/* Fetch the current call frame. That determines context. */
Tcl_CallFrame *framePtr = Itcl_GetUplevelCallFrame(interp, 0);
/* Try to map it to a context stack. */
infoPtr = (ItclObjectInfo *)Tcl_GetAssocData(interp,
ITCL_INTERP_DATA, NULL);
hPtr = Tcl_FindHashEntry(&infoPtr->frameContext, (char *)framePtr);
if (hPtr == NULL) {
/* Can this happen? */
return;
}
/* Frame maps to a context stack. */
stackPtr = (Itcl_Stack *)Tcl_GetHashValue(hPtr);
callContextPtr = (ItclCallContext *)Itcl_PeekStack(stackPtr);
if (callContextPtr == NULL) {
return;
}
currIclsPtr = NULL;
objPtr = NULL;
{
imPtr = callContextPtr->imPtr;
contextIoPtr = callContextPtr->ioPtr;
objPtr = Tcl_NewStringObj("\n ", -1);
if (imPtr->flags & ITCL_CONSTRUCTOR) {
currIclsPtr = imPtr->iclsPtr;
Tcl_AppendToObj(objPtr, "while constructing object \"", -1);
Tcl_GetCommandFullName(interp, contextIoPtr->accessCmd, objPtr);
Tcl_AppendToObj(objPtr, "\" in ", -1);
Tcl_AppendToObj(objPtr, currIclsPtr->nsPtr->fullName, -1);
Tcl_AppendToObj(objPtr, "::constructor", -1);
if ((imPtr->codePtr->flags & ITCL_IMPLEMENT_TCL) != 0) {
Tcl_AppendToObj(objPtr, " (", -1);
}
}
if (imPtr->flags & ITCL_DESTRUCTOR) {
contextIoPtr->flags = 0;
Tcl_AppendToObj(objPtr, "while deleting object \"", -1);
Tcl_GetCommandFullName(interp, contextIoPtr->accessCmd, objPtr);
Tcl_AppendToObj(objPtr, "\" in ", -1);
Tcl_AppendToObj(objPtr, Tcl_GetString(imPtr->fullNamePtr), -1);
if ((imPtr->codePtr->flags & ITCL_IMPLEMENT_TCL) != 0) {
Tcl_AppendToObj(objPtr, " (", -1);
}
}
if (!(imPtr->flags & (ITCL_CONSTRUCTOR|ITCL_DESTRUCTOR))) {
Tcl_AppendToObj(objPtr, "(", -1);
hPtr = Tcl_FindHashEntry(&infoPtr->objects, (char *)contextIoPtr);
if (hPtr != NULL) {
if ((contextIoPtr != NULL) && (contextIoPtr->accessCmd)) {
Tcl_AppendToObj(objPtr, "object \"", -1);
Tcl_GetCommandFullName(interp, contextIoPtr->accessCmd, objPtr);
Tcl_AppendToObj(objPtr, "\" ", -1);
}
}
if ((imPtr->flags & ITCL_COMMON) != 0) {
Tcl_AppendToObj(objPtr, "procedure", -1);
} else {
Tcl_AppendToObj(objPtr, "method", -1);
}
Tcl_AppendToObj(objPtr, " \"", -1);
Tcl_AppendToObj(objPtr, Tcl_GetString(imPtr->fullNamePtr), -1);
Tcl_AppendToObj(objPtr, "\" ", -1);
}
if ((imPtr->codePtr->flags & ITCL_IMPLEMENT_TCL) != 0) {
Tcl_Obj *dictPtr;
Tcl_Obj *keyPtr;
Tcl_Obj *valuePtr;
int lineNo;
keyPtr = Tcl_NewStringObj("-errorline", -1);
dictPtr = Tcl_GetReturnOptions(interp, TCL_ERROR);
if (Tcl_DictObjGet(interp, dictPtr, keyPtr, &valuePtr) != TCL_OK) {
/* how should we handle an error ? */
Tcl_DecrRefCount(dictPtr);
Tcl_DecrRefCount(keyPtr);
Tcl_DecrRefCount(objPtr);
return;
}
if (valuePtr == NULL) {
/* how should we handle an error ? */
Tcl_DecrRefCount(dictPtr);
Tcl_DecrRefCount(keyPtr);
Tcl_DecrRefCount(objPtr);
return;
}
if (Tcl_GetIntFromObj(interp, valuePtr, &lineNo) != TCL_OK) {
/* how should we handle an error ? */
Tcl_DecrRefCount(dictPtr);
Tcl_DecrRefCount(keyPtr);
Tcl_DecrRefCount(objPtr);
return;
}
Tcl_DecrRefCount(dictPtr);
Tcl_DecrRefCount(keyPtr);
Tcl_AppendToObj(objPtr, "body line ", -1);
sprintf(num, "%d", lineNo);
Tcl_AppendToObj(objPtr, num, -1);
Tcl_AppendToObj(objPtr, ")", -1);
} else {
Tcl_AppendToObj(objPtr, ")", -1);
}
Tcl_AppendObjToErrorInfo(interp, objPtr);
objPtr = NULL;
}
if (objPtr != NULL) {
Tcl_DecrRefCount(objPtr);
}
}