diff options
Diffstat (limited to 'generic/tclOOBasic.c')
-rw-r--r-- | generic/tclOOBasic.c | 559 |
1 files changed, 407 insertions, 152 deletions
diff --git a/generic/tclOOBasic.c b/generic/tclOOBasic.c index 2adf547..0b0516b 100644 --- a/generic/tclOOBasic.c +++ b/generic/tclOOBasic.c @@ -4,12 +4,10 @@ * This file contains implementations of the "simple" commands and * methods from the object-system core. * - * Copyright (c) 2005-2008 by Donal K. Fellows + * Copyright (c) 2005-2013 by Donal K. Fellows * * See the file "license.terms" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. - * - * RCS: @(#) $Id: tclOOBasic.c,v 1.7 2008/07/18 23:29:44 msofer Exp $ */ #ifdef HAVE_CONFIG_H @@ -17,15 +15,13 @@ #endif #include "tclInt.h" #include "tclOOInt.h" -#include "tclNRE.h" static inline Tcl_Object *AddConstructionFinalizer(Tcl_Interp *interp); -static int FinalizeConstruction(ClientData data[], - Tcl_Interp *interp, int result); -static int FinalizeEval(ClientData data[], - Tcl_Interp *interp, int result); -static int RestoreFrame(ClientData data[], - Tcl_Interp *interp, int result); +static Tcl_NRPostProc AfterNRDestructor; +static Tcl_NRPostProc DecrRefsPostClassConstructor; +static Tcl_NRPostProc FinalizeConstruction; +static Tcl_NRPostProc FinalizeEval; +static Tcl_NRPostProc NextRestoreFrame; /* * ---------------------------------------------------------------------- @@ -39,8 +35,8 @@ static int RestoreFrame(ClientData data[], * createWithNamespace, new). * * Note that this is the only code in this file (or, indeed, the whole of - * TclOO) that uses tclNRE.h; it is the only code that does non-standard - * poking in the NRE guts. + * TclOO) that uses NRE internals; it is the only code that does + * non-standard poking in the NRE guts. * * ---------------------------------------------------------------------- */ @@ -49,11 +45,8 @@ static inline Tcl_Object * AddConstructionFinalizer( Tcl_Interp *interp) { - TEOV_record *recordPtr; - TclNRAddCallback(interp, FinalizeConstruction, NULL, NULL, NULL, NULL); - recordPtr = TOP_RECORD(interp); - return (Tcl_Object *) &recordPtr->callbackPtr->data[0]; + return (Tcl_Object *) &(TOP_CB(interp)->data[0]); } static int @@ -74,6 +67,78 @@ FinalizeConstruction( /* * ---------------------------------------------------------------------- * + * TclOO_Class_Constructor -- + * + * Implementation for oo::class constructor. + * + * ---------------------------------------------------------------------- + */ + +int +TclOO_Class_Constructor( + ClientData clientData, + Tcl_Interp *interp, + Tcl_ObjectContext context, + int objc, + Tcl_Obj *const *objv) +{ + Object *oPtr = (Object *) Tcl_ObjectContextObject(context); + Tcl_Obj **invoke; + + if (objc-1 > Tcl_ObjectContextSkippedArgs(context)) { + Tcl_WrongNumArgs(interp, Tcl_ObjectContextSkippedArgs(context), objv, + "?definitionScript?"); + return TCL_ERROR; + } else if (objc == Tcl_ObjectContextSkippedArgs(context)) { + return TCL_OK; + } + + /* + * Delegate to [oo::define] to do the work. + */ + + invoke = ckalloc(3 * sizeof(Tcl_Obj *)); + invoke[0] = oPtr->fPtr->defineName; + invoke[1] = TclOOObjectName(interp, oPtr); + invoke[2] = objv[objc-1]; + + /* + * Must add references or errors in configuration script will cause + * trouble. + */ + + Tcl_IncrRefCount(invoke[0]); + Tcl_IncrRefCount(invoke[1]); + Tcl_IncrRefCount(invoke[2]); + TclNRAddCallback(interp, DecrRefsPostClassConstructor, + invoke, NULL, NULL, NULL); + + /* + * Tricky point: do not want the extra reported level in the Tcl stack + * trace, so use TCL_EVAL_NOERR. + */ + + return TclNREvalObjv(interp, 3, invoke, TCL_EVAL_NOERR, NULL); +} + +static int +DecrRefsPostClassConstructor( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + Tcl_Obj **invoke = data[0]; + + TclDecrRefCount(invoke[0]); + TclDecrRefCount(invoke[1]); + TclDecrRefCount(invoke[2]); + ckfree(invoke); + return result; +} + +/* + * ---------------------------------------------------------------------- + * * TclOO_Class_Create -- * * Implementation for oo::class->create method. @@ -102,8 +167,9 @@ TclOO_Class_Create( if (oPtr->classPtr == NULL) { Tcl_Obj *cmdnameObj = TclOOObjectName(interp, oPtr); - Tcl_AppendResult(interp, "object \"", TclGetString(cmdnameObj), - "\" is not a class", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "object \"%s\" is not a class", TclGetString(cmdnameObj))); + Tcl_SetErrorCode(interp, "TCL", "OO", "INSTANTIATE_NONCLASS", NULL); return TCL_ERROR; } @@ -119,7 +185,9 @@ TclOO_Class_Create( objName = Tcl_GetStringFromObj( objv[Tcl_ObjectContextSkippedArgs(context)], &len); if (len == 0) { - Tcl_AppendResult(interp, "object name must not be empty", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "object name must not be empty", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "EMPTY_NAME", NULL); return TCL_ERROR; } @@ -164,8 +232,9 @@ TclOO_Class_CreateNs( if (oPtr->classPtr == NULL) { Tcl_Obj *cmdnameObj = TclOOObjectName(interp, oPtr); - Tcl_AppendResult(interp, "object \"", TclGetString(cmdnameObj), - "\" is not a class", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "object \"%s\" is not a class", TclGetString(cmdnameObj))); + Tcl_SetErrorCode(interp, "TCL", "OO", "INSTANTIATE_NONCLASS", NULL); return TCL_ERROR; } @@ -181,13 +250,17 @@ TclOO_Class_CreateNs( objName = Tcl_GetStringFromObj( objv[Tcl_ObjectContextSkippedArgs(context)], &len); if (len == 0) { - Tcl_AppendResult(interp, "object name must not be empty", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "object name must not be empty", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "EMPTY_NAME", NULL); return TCL_ERROR; } nsName = Tcl_GetStringFromObj( objv[Tcl_ObjectContextSkippedArgs(context)+1], &len); if (len == 0) { - Tcl_AppendResult(interp, "namespace name must not be empty", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "namespace name must not be empty", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "EMPTY_NAME", NULL); return TCL_ERROR; } @@ -230,8 +303,9 @@ TclOO_Class_New( if (oPtr->classPtr == NULL) { Tcl_Obj *cmdnameObj = TclOOObjectName(interp, oPtr); - Tcl_AppendResult(interp, "object \"", TclGetString(cmdnameObj), - "\" is not a class", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "object \"%s\" is not a class", TclGetString(cmdnameObj))); + Tcl_SetErrorCode(interp, "TCL", "OO", "INSTANTIATE_NONCLASS", NULL); return TCL_ERROR; } @@ -263,15 +337,46 @@ TclOO_Object_Destroy( int objc, /* Number of arguments. */ Tcl_Obj *const *objv) /* The actual arguments. */ { + Object *oPtr = (Object *) Tcl_ObjectContextObject(context); + CallContext *contextPtr; + if (objc != Tcl_ObjectContextSkippedArgs(context)) { Tcl_WrongNumArgs(interp, Tcl_ObjectContextSkippedArgs(context), objv, NULL); return TCL_ERROR; } - Tcl_DeleteCommandFromToken(interp, - Tcl_GetObjectCommand(Tcl_ObjectContextObject(context))); + if (!(oPtr->flags & DESTRUCTOR_CALLED)) { + oPtr->flags |= DESTRUCTOR_CALLED; + contextPtr = TclOOGetCallContext(oPtr, NULL, DESTRUCTOR, NULL); + if (contextPtr != NULL) { + contextPtr->callPtr->flags |= DESTRUCTOR; + contextPtr->skip = 0; + TclNRAddCallback(interp, AfterNRDestructor, contextPtr, + NULL, NULL, NULL); + TclPushTailcallPoint(interp); + return TclOOInvokeContext(contextPtr, interp, 0, NULL); + } + } + if (oPtr->command) { + Tcl_DeleteCommandFromToken(interp, oPtr->command); + } return TCL_OK; } + +static int +AfterNRDestructor( + ClientData data[], + Tcl_Interp *interp, + int result) +{ + CallContext *contextPtr = data[0]; + + if (contextPtr->oPtr->command) { + Tcl_DeleteCommandFromToken(interp, contextPtr->oPtr->command); + } + TclOODeleteContext(contextPtr); + return result; +} /* * ---------------------------------------------------------------------- @@ -297,7 +402,7 @@ TclOO_Object_Eval( register const int skip = Tcl_ObjectContextSkippedArgs(context); CallFrame *framePtr, **framePtrPtr = &framePtr; Tcl_Obj *scriptPtr; - int result, flags; + int result; CmdFrame *invoker; if (objc-1 < skip) { @@ -333,11 +438,9 @@ TclOO_Object_Eval( if (objc != skip+1) { scriptPtr = Tcl_ConcatObj(objc-skip, objv+skip); - flags = TCL_EVAL_DIRECT; invoker = NULL; } else { scriptPtr = objv[skip]; - flags = 0; invoker = ((Interp *) interp)->cmdFramePtr; } @@ -347,7 +450,7 @@ TclOO_Object_Eval( */ TclNRAddCallback(interp, FinalizeEval, object, NULL, NULL, NULL); - return TclNREvalObjEx(interp, scriptPtr, flags, invoker, skip); + return TclNREvalObjEx(interp, scriptPtr, 0, invoker, skip); } static int @@ -358,18 +461,17 @@ FinalizeEval( { if (result == TCL_ERROR) { Object *oPtr = data[0]; + const char *namePtr; if (oPtr) { - Tcl_Obj *objnameObj = TclOOObjectName(interp, oPtr); - - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (in \"%s eval\" script line %d)", - TclGetString(objnameObj), interp->errorLine)); + namePtr = TclGetString(TclOOObjectName(interp, oPtr)); } else { - Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( - "\n (in \"my eval\" script line %d)", - interp->errorLine)); + namePtr = "my"; } + + Tcl_AppendObjToErrorInfo(interp, Tcl_ObjPrintf( + "\n (in \"%s eval\" script line %d)", + namePtr, Tcl_GetErrorLine(interp))); } /* @@ -404,9 +506,16 @@ TclOO_Object_Unknown( Object *oPtr = contextPtr->oPtr; const char **methodNames; int numMethodNames, i, skip = Tcl_ObjectContextSkippedArgs(context); + Tcl_Obj *errorMsg; + + /* + * If no method name, generate an error asking for a method name. (Only by + * overriding *this* method can an object handle the absence of a method + * name without an error). + */ if (objc < skip+1) { - Tcl_WrongNumArgs(interp, skip, objv, "methodName ?arg ...?"); + Tcl_WrongNumArgs(interp, skip, objv, "method ?arg ...?"); return TCL_ERROR; } @@ -423,29 +532,36 @@ TclOO_Object_Unknown( if (numMethodNames == 0) { Tcl_Obj *tmpBuf = TclOOObjectName(interp, oPtr); + const char *piece; - Tcl_AppendResult(interp, "object \"", TclGetString(tmpBuf), NULL); if (contextPtr->callPtr->flags & PUBLIC_METHOD) { - Tcl_AppendResult(interp, "\" has no visible methods", NULL); + piece = "visible methods"; } else { - Tcl_AppendResult(interp, "\" has no methods", NULL); + piece = "methods"; } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "object \"%s\" has no %s", TclGetString(tmpBuf), piece)); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "METHOD", + TclGetString(objv[skip]), NULL); return TCL_ERROR; } - Tcl_AppendResult(interp, "unknown method \"", TclGetString(objv[skip]), - "\": must be ", NULL); + errorMsg = Tcl_ObjPrintf("unknown method \"%s\": must be ", + TclGetString(objv[skip])); for (i=0 ; i<numMethodNames-1 ; i++) { if (i) { - Tcl_AppendResult(interp, ", ", NULL); + Tcl_AppendToObj(errorMsg, ", ", -1); } - Tcl_AppendResult(interp, methodNames[i], NULL); + Tcl_AppendToObj(errorMsg, methodNames[i], -1); } if (i) { - Tcl_AppendResult(interp, " or ", NULL); + Tcl_AppendToObj(errorMsg, " or ", -1); } - Tcl_AppendResult(interp, methodNames[i], NULL); - ckfree((char *) methodNames); + Tcl_AppendToObj(errorMsg, methodNames[i], -1); + ckfree(methodNames); + Tcl_SetObjResult(interp, errorMsg); + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "METHOD", + TclGetString(objv[skip]), NULL); return TCL_ERROR; } @@ -473,19 +589,19 @@ TclOO_Object_LinkVar( Namespace *savedNsPtr; int i; - if (objc-Tcl_ObjectContextSkippedArgs(context) < 1) { + if (objc-Tcl_ObjectContextSkippedArgs(context) < 0) { Tcl_WrongNumArgs(interp, Tcl_ObjectContextSkippedArgs(context), objv, - "varName ?varName ...?"); + "?varName ...?"); return TCL_ERROR; } /* - * Do nothing if we are not called from the body of a method. In this - * respect, we are like the [global] command. + * A sanity check. Shouldn't ever happen. (This is all that remains of a + * more complex check inherited from [global] after we have applied the + * fix for [Bug 2903811]; note that the fix involved *removing* code.) */ - if (iPtr->varFramePtr == NULL || - !(iPtr->varFramePtr->isProcCallFrame & FRAME_IS_METHOD)) { + if (iPtr->varFramePtr == NULL) { return TCL_OK; } @@ -499,8 +615,10 @@ TclOO_Object_LinkVar( */ if (strstr(varName, "::") != NULL) { - Tcl_AppendResult(interp, "variable name \"", varName, - "\" illegal: must not contain namespace separator", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "variable name \"%s\" illegal: must not contain namespace" + " separator", varName)); + Tcl_SetErrorCode(interp, "TCL", "UPVAR", "INVERTED", NULL); return TCL_ERROR; } @@ -511,9 +629,7 @@ TclOO_Object_LinkVar( * would only work if the caller was a method of the object itself, * which might not be true if the method was exported. This is a bit * of a hack, but the simplest way to do this (pushing a stack frame - * would be horribly expensive by comparison). We never have to worry - * about the case where we're dealing with the global namespace; we've - * already checked that we are inside a method. + * would be horribly expensive by comparison). */ savedNsPtr = iPtr->varFramePtr->nsPtr; @@ -531,6 +647,7 @@ TclOO_Object_LinkVar( TclVarErrMsg(interp, varName, NULL, "define", "name refers to an element in an array"); + Tcl_SetErrorCode(interp, "TCL", "UPVAR", "LOCAL_ELEMENT", NULL); return TCL_ERROR; } @@ -569,52 +686,76 @@ TclOO_Object_VarName( int objc, /* Number of arguments. */ Tcl_Obj *const *objv) /* The actual arguments. */ { - Interp *iPtr = (Interp *) interp; Var *varPtr, *aryVar; - Tcl_Obj *varNamePtr; + Tcl_Obj *varNamePtr, *argPtr; + const char *arg; if (Tcl_ObjectContextSkippedArgs(context)+1 != objc) { Tcl_WrongNumArgs(interp, Tcl_ObjectContextSkippedArgs(context), objv, "varName"); return TCL_ERROR; } + argPtr = objv[objc-1]; + arg = Tcl_GetString(argPtr); /* - * Switch to the object's namespace for the duration of this call. Like - * this, the variable is looked up in the namespace of the object, and not - * in the namespace of the caller. Otherwise this would only work if the - * caller was a method of the object itself, which might not be true if - * the method was exported. This is a bit of a hack, but the simplest way - * to do this (pushing a stack frame would be horribly expensive by - * comparison, and is only done when we'd otherwise interfere with the - * global namespace). + * Convert the variable name to fully-qualified form if it wasn't already. + * This has to be done prior to lookup because we can run into problems + * with resolvers otherwise. [Bug 3603695] + * + * We still need to do the lookup; the variable could be linked to another + * variable and we want the target's name. */ - if (iPtr->varFramePtr == NULL) { - Tcl_CallFrame *dummyFrame; - - TclPushStackFrame(interp, &dummyFrame, - Tcl_GetObjectNamespace(Tcl_ObjectContextObject(context)),0); - varPtr = TclObjLookupVar(interp, objv[objc-1], NULL, - TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG, "refer to",1,1,&aryVar); - TclPopStackFrame(interp); + if (arg[0] == ':' && arg[1] == ':') { + varNamePtr = argPtr; } else { - Namespace *savedNsPtr; - - savedNsPtr = iPtr->varFramePtr->nsPtr; - iPtr->varFramePtr->nsPtr = (Namespace *) + Tcl_Namespace *namespacePtr = Tcl_GetObjectNamespace(Tcl_ObjectContextObject(context)); - varPtr = TclObjLookupVar(interp, objv[objc-1], NULL, - TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG, "refer to",1,1,&aryVar); - iPtr->varFramePtr->nsPtr = savedNsPtr; - } + varNamePtr = Tcl_NewStringObj(namespacePtr->fullName, -1); + Tcl_AppendToObj(varNamePtr, "::", 2); + Tcl_AppendObjToObj(varNamePtr, argPtr); + } + Tcl_IncrRefCount(varNamePtr); + varPtr = TclObjLookupVar(interp, varNamePtr, NULL, + TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG, "refer to", 1, 1, &aryVar); + Tcl_DecrRefCount(varNamePtr); if (varPtr == NULL) { + Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "VARIABLE", arg, NULL); return TCL_ERROR; } + /* + * Now that we've pinned down what variable we're really talking about + * (including traversing variable links), convert back to a name. + */ + varNamePtr = Tcl_NewObj(); - Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, varNamePtr); + if (aryVar != NULL) { + Tcl_HashEntry *hPtr; + Tcl_HashSearch search; + + Tcl_GetVariableFullName(interp, (Tcl_Var) aryVar, varNamePtr); + + /* + * WARNING! This code pokes inside the implementation of hash tables! + */ + + hPtr = Tcl_FirstHashEntry((Tcl_HashTable *) aryVar->value.tablePtr, + &search); + while (hPtr != NULL) { + if (varPtr == Tcl_GetHashValue(hPtr)) { + Tcl_AppendToObj(varNamePtr, "(", -1); + Tcl_AppendObjToObj(varNamePtr, hPtr->key.objPtr); + Tcl_AppendToObj(varNamePtr, ")", -1); + break; + } + hPtr = Tcl_NextHashEntry(&search); + } + } else { + Tcl_GetVariableFullName(interp, (Tcl_Var) varPtr, varNamePtr); + } Tcl_SetObjResult(interp, varNamePtr); return TCL_OK; } @@ -622,10 +763,11 @@ TclOO_Object_VarName( /* * ---------------------------------------------------------------------- * - * TclOONextObjCmd -- + * TclOONextObjCmd, TclOONextToObjCmd -- * - * Implementation of the [next] command. Note that this command is only - * ever to be used inside the body of a procedure-like method. + * Implementation of the [next] and [nextto] commands. Note that these + * commands are only ever to be used inside the body of a procedure-like + * method. * * ---------------------------------------------------------------------- */ @@ -648,8 +790,10 @@ TclOONextObjCmd( */ if (framePtr == NULL || !(framePtr->isProcCallFrame & FRAME_IS_METHOD)) { - Tcl_AppendResult(interp, TclGetString(objv[0]), - " may only be called from inside a method", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s may only be called from inside a method", + TclGetString(objv[0]))); + Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL); return TCL_ERROR; } context = framePtr->clientData; @@ -659,20 +803,130 @@ TclOONextObjCmd( * that this is like [uplevel 1] and not [eval]. */ - TclNRAddCallback(interp, RestoreFrame, framePtr, NULL, NULL, NULL); + TclNRAddCallback(interp, NextRestoreFrame, framePtr, NULL,NULL,NULL); iPtr->varFramePtr = framePtr->callerVarPtr; return TclNRObjectContextInvokeNext(interp, context, objc, objv, 1); } +int +TclOONextToObjCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) +{ + Interp *iPtr = (Interp *) interp; + CallFrame *framePtr = iPtr->varFramePtr; + Class *classPtr; + CallContext *contextPtr; + int i; + Tcl_Object object; + const char *methodType; + + /* + * Start with sanity checks on the calling context to make sure that we + * are invoked from a suitable method context. If so, we can safely + * retrieve the handle to the object call context. + */ + + if (framePtr == NULL || !(framePtr->isProcCallFrame & FRAME_IS_METHOD)) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s may only be called from inside a method", + TclGetString(objv[0]))); + Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL); + return TCL_ERROR; + } + contextPtr = framePtr->clientData; + + /* + * Sanity check the arguments; we need the first one to refer to a class. + */ + + if (objc < 2) { + Tcl_WrongNumArgs(interp, 1, objv, "class ?arg...?"); + return TCL_ERROR; + } + object = Tcl_GetObjectFromObj(interp, objv[1]); + if (object == NULL) { + return TCL_ERROR; + } + classPtr = ((Object *)object)->classPtr; + if (classPtr == NULL) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "\"%s\" is not a class", TclGetString(objv[1]))); + Tcl_SetErrorCode(interp, "TCL", "OO", "CLASS_REQUIRED", NULL); + return TCL_ERROR; + } + + /* + * Search for an implementation of a method associated with the current + * call on the call chain past the point where we currently are. Do not + * allow jumping backwards! + */ + + for (i=contextPtr->index+1 ; i<contextPtr->callPtr->numChain ; i++) { + struct MInvoke *miPtr = contextPtr->callPtr->chain + i; + + if (!miPtr->isFilter && miPtr->mPtr->declaringClassPtr == classPtr) { + /* + * Invoke the (advanced) method call context in the caller + * context. Note that this is like [uplevel 1] and not [eval]. + */ + + TclNRAddCallback(interp, NextRestoreFrame, framePtr, + contextPtr, INT2PTR(contextPtr->index), NULL); + contextPtr->index = i-1; + iPtr->varFramePtr = framePtr->callerVarPtr; + return TclNRObjectContextInvokeNext(interp, + (Tcl_ObjectContext) contextPtr, objc, objv, 2); + } + } + + /* + * Generate an appropriate error message, depending on whether the value + * is on the chain but unreachable, or not on the chain at all. + */ + + if (contextPtr->callPtr->flags & CONSTRUCTOR) { + methodType = "constructor"; + } else if (contextPtr->callPtr->flags & DESTRUCTOR) { + methodType = "destructor"; + } else { + methodType = "method"; + } + + for (i=contextPtr->index ; i>=0 ; i--) { + struct MInvoke *miPtr = contextPtr->callPtr->chain + i; + + if (!miPtr->isFilter && miPtr->mPtr->declaringClassPtr == classPtr) { + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s implementation by \"%s\" not reachable from here", + methodType, TclGetString(objv[1]))); + Tcl_SetErrorCode(interp, "TCL", "OO", "CLASS_NOT_REACHABLE", + NULL); + return TCL_ERROR; + } + } + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s has no non-filter implementation by \"%s\"", + methodType, TclGetString(objv[1]))); + Tcl_SetErrorCode(interp, "TCL", "OO", "CLASS_NOT_THERE", NULL); + return TCL_ERROR; +} + static int -RestoreFrame( +NextRestoreFrame( ClientData data[], Tcl_Interp *interp, int result) { Interp *iPtr = (Interp *) interp; + CallContext *contextPtr = data[1]; iPtr->varFramePtr = data[0]; + if (contextPtr != NULL) { + contextPtr->index = PTR2INT(data[2]); + } return result; } @@ -694,17 +948,18 @@ TclOOSelfObjCmd( int objc, Tcl_Obj *const *objv) { - static const char *subcmds[] = { - "caller", "class", "filter", "method", "namespace", "next", "object", - "target", NULL + static const char *const subcmds[] = { + "call", "caller", "class", "filter", "method", "namespace", "next", + "object", "target", NULL }; enum SelfCmds { - SELF_CALLER, SELF_CLASS, SELF_FILTER, SELF_METHOD, SELF_NS, SELF_NEXT, - SELF_OBJECT, SELF_TARGET + SELF_CALL, SELF_CALLER, SELF_CLASS, SELF_FILTER, SELF_METHOD, SELF_NS, + SELF_NEXT, SELF_OBJECT, SELF_TARGET }; Interp *iPtr = (Interp *) interp; CallFrame *framePtr = iPtr->varFramePtr; CallContext *contextPtr; + Tcl_Obj *result[3]; int index; #define CurrentlyInvoked(contextPtr) \ @@ -715,8 +970,10 @@ TclOOSelfObjCmd( */ if (framePtr == NULL || !(framePtr->isProcCallFrame & FRAME_IS_METHOD)) { - Tcl_AppendResult(interp, TclGetString(objv[0]), - " may only be called from inside a method", NULL); + Tcl_SetObjResult(interp, Tcl_ObjPrintf( + "%s may only be called from inside a method", + TclGetString(objv[0]))); + Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL); return TCL_ERROR; } @@ -746,30 +1003,23 @@ TclOOSelfObjCmd( contextPtr->oPtr->namespacePtr->fullName,-1)); return TCL_OK; case SELF_CLASS: { - Method *mPtr = CurrentlyInvoked(contextPtr).mPtr; - Object *declarerPtr; + Class *clsPtr = CurrentlyInvoked(contextPtr).mPtr->declaringClassPtr; - if (mPtr->declaringClassPtr != NULL) { - declarerPtr = mPtr->declaringClassPtr->thisPtr; - } else if (mPtr->declaringObjectPtr != NULL) { - declarerPtr = mPtr->declaringObjectPtr; - } else { - /* - * This should be unreachable code. - */ - - Tcl_AppendResult(interp, "method without declarer!", NULL); + if (clsPtr == NULL) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "method not defined by a class", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "UNMATCHED_CONTEXT", NULL); return TCL_ERROR; } - Tcl_SetObjResult(interp, TclOOObjectName(interp, declarerPtr)); + Tcl_SetObjResult(interp, TclOOObjectName(interp, clsPtr->thisPtr)); return TCL_OK; } case SELF_METHOD: if (contextPtr->callPtr->flags & CONSTRUCTOR) { - Tcl_AppendResult(interp, "<constructor>", NULL); + Tcl_SetObjResult(interp, contextPtr->oPtr->fPtr->constructorName); } else if (contextPtr->callPtr->flags & DESTRUCTOR) { - Tcl_AppendResult(interp, "<destructor>", NULL); + Tcl_SetObjResult(interp, contextPtr->oPtr->fPtr->destructorName); } else { Tcl_SetObjResult(interp, CurrentlyInvoked(contextPtr).mPtr->namePtr); @@ -777,11 +1027,12 @@ TclOOSelfObjCmd( return TCL_OK; case SELF_FILTER: if (!CurrentlyInvoked(contextPtr).isFilter) { - Tcl_AppendResult(interp, "not inside a filtering context", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "not inside a filtering context", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "UNMATCHED_CONTEXT", NULL); return TCL_ERROR; } else { register struct MInvoke *miPtr = &CurrentlyInvoked(contextPtr); - Tcl_Obj *result[3]; Object *oPtr; const char *type; @@ -800,8 +1051,13 @@ TclOOSelfObjCmd( return TCL_OK; } case SELF_CALLER: - if ((framePtr->callerVarPtr != NULL) && - (framePtr->callerVarPtr->isProcCallFrame & FRAME_IS_METHOD)) { + if ((framePtr->callerVarPtr == NULL) || + !(framePtr->callerVarPtr->isProcCallFrame & FRAME_IS_METHOD)){ + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "caller is not an object", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "CONTEXT_REQUIRED", NULL); + return TCL_ERROR; + } else { CallContext *callerPtr = framePtr->callerVarPtr->clientData; Method *mPtr = callerPtr->callPtr->chain[callerPtr->index].mPtr; Object *declarerPtr; @@ -815,28 +1071,22 @@ TclOOSelfObjCmd( * This should be unreachable code. */ - Tcl_AppendResult(interp, "method without declarer!", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "method without declarer!", -1)); return TCL_ERROR; } - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - TclOOObjectName(interp, declarerPtr)); - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - TclOOObjectName(interp, callerPtr->oPtr)); + result[0] = TclOOObjectName(interp, declarerPtr); + result[1] = TclOOObjectName(interp, callerPtr->oPtr); if (callerPtr->callPtr->flags & CONSTRUCTOR) { - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - Tcl_NewStringObj("<constructor>", -1)); + result[2] = declarerPtr->fPtr->constructorName; } else if (callerPtr->callPtr->flags & DESTRUCTOR) { - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - Tcl_NewStringObj("<destructor>", -1)); + result[2] = declarerPtr->fPtr->destructorName; } else { - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - mPtr->namePtr); + result[2] = mPtr->namePtr; } + Tcl_SetObjResult(interp, Tcl_NewListObj(3, result)); return TCL_OK; - } else { - Tcl_AppendResult(interp, "caller is not an object", NULL); - return TCL_ERROR; } case SELF_NEXT: if (contextPtr->index < contextPtr->callPtr->numChain-1) { @@ -853,27 +1103,27 @@ TclOOSelfObjCmd( * This should be unreachable code. */ - Tcl_AppendResult(interp, "method without declarer!", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "method without declarer!", -1)); return TCL_ERROR; } - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - TclOOObjectName(interp, declarerPtr)); + result[0] = TclOOObjectName(interp, declarerPtr); if (contextPtr->callPtr->flags & CONSTRUCTOR) { - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - Tcl_NewStringObj("<constructor>", -1)); + result[1] = declarerPtr->fPtr->constructorName; } else if (contextPtr->callPtr->flags & DESTRUCTOR) { - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - Tcl_NewStringObj("<destructor>", -1)); + result[1] = declarerPtr->fPtr->destructorName; } else { - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - mPtr->namePtr); + result[1] = mPtr->namePtr; } + Tcl_SetObjResult(interp, Tcl_NewListObj(2, result)); } return TCL_OK; case SELF_TARGET: if (!CurrentlyInvoked(contextPtr).isFilter) { - Tcl_AppendResult(interp, "not inside a filtering context", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "not inside a filtering context", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "UNMATCHED_CONTEXT", NULL); return TCL_ERROR; } else { Method *mPtr; @@ -898,15 +1148,20 @@ TclOOSelfObjCmd( * This should be unreachable code. */ - Tcl_AppendResult(interp, "method without declarer!", NULL); + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "method without declarer!", -1)); return TCL_ERROR; } - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - TclOOObjectName(interp, declarerPtr)); - Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), - mPtr->namePtr); + result[0] = TclOOObjectName(interp, declarerPtr); + result[1] = mPtr->namePtr; + Tcl_SetObjResult(interp, Tcl_NewListObj(2, result)); return TCL_OK; } + case SELF_CALL: + result[0] = TclOORenderCallChain(interp, contextPtr->callPtr); + result[1] = Tcl_NewIntObj(contextPtr->index); + Tcl_SetObjResult(interp, Tcl_NewListObj(2, result)); + return TCL_OK; } return TCL_ERROR; } @@ -953,7 +1208,7 @@ TclOOCopyObjectCmd( if (objc == 2) { o2Ptr = Tcl_CopyObjectInstance(interp, oPtr, NULL, NULL); } else { - char *name; + const char *name; Tcl_DString buffer; name = TclGetString(objv[2]); @@ -965,7 +1220,7 @@ TclOOCopyObjectCmd( Tcl_DStringAppend(&buffer, iPtr->varFramePtr->nsPtr->fullName, -1); } - Tcl_DStringAppend(&buffer, "::", 2); + TclDStringAppendLiteral(&buffer, "::"); Tcl_DStringAppend(&buffer, name, -1); name = Tcl_DStringValue(&buffer); } |