summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjan.nijtmans <nijtmans@users.sourceforge.net>2025-08-25 11:16:05 (GMT)
committerjan.nijtmans <nijtmans@users.sourceforge.net>2025-08-25 11:16:05 (GMT)
commitb33dac7519bc4a79f37f10c65a6a620ec9b0eee2 (patch)
treed10c2014ad707c9ac9cf19a29da8d2da13b1e6f8
parentdace3996715c6a8f97b7c0a89e849df13c2be5f9 (diff)
parent42725da7ba8157864eeb3c79a3cb3cfde0d8efb6 (diff)
downloadtcl-b33dac7519bc4a79f37f10c65a6a620ec9b0eee2.zip
tcl-b33dac7519bc4a79f37f10c65a6a620ec9b0eee2.tar.gz
tcl-b33dac7519bc4a79f37f10c65a6a620ec9b0eee2.tar.bz2
Merge trunk
-rw-r--r--generic/tclInt.h4
-rw-r--r--generic/tclInterp.c20
-rw-r--r--generic/tclOO.c150
-rw-r--r--generic/tclOOBasic.c376
-rw-r--r--generic/tclOODefineCmds.c906
-rw-r--r--generic/tclOOInt.h11
-rw-r--r--generic/tclOOScript.h220
-rw-r--r--generic/tclStringObj.c4
-rw-r--r--tests/oo.test2
-rw-r--r--tools/tclOOScript.tcl377
-rw-r--r--win/Makefile.in2
11 files changed, 1361 insertions, 711 deletions
diff --git a/generic/tclInt.h b/generic/tclInt.h
index 096d5e7..9252eb8 100644
--- a/generic/tclInt.h
+++ b/generic/tclInt.h
@@ -3282,6 +3282,10 @@ MODULE_SCOPE void TclAdvanceContinuations(int *line, Tcl_Size **next,
Tcl_Size loc);
MODULE_SCOPE void TclAdvanceLines(int *line, const char *start,
const char *end);
+MODULE_SCOPE int TclAliasCreate(Tcl_Interp *interp,
+ Tcl_Interp *childInterp, Tcl_Interp *parentInterp,
+ Tcl_Obj *namePtr, Tcl_Obj *targetPtr, Tcl_Size objc,
+ Tcl_Obj *const objv[]);
MODULE_SCOPE void TclAppendBytesToByteArray(Tcl_Obj *objPtr,
const unsigned char *bytes, Tcl_Size len);
MODULE_SCOPE void TclAppendUtfToUtf(Tcl_Obj *objPtr,
diff --git a/generic/tclInterp.c b/generic/tclInterp.c
index 061ddcf..77d06f6 100644
--- a/generic/tclInterp.c
+++ b/generic/tclInterp.c
@@ -221,10 +221,6 @@ enum LimitHandlerFlags {
* Prototypes for local static functions:
*/
-static int AliasCreate(Tcl_Interp *interp,
- Tcl_Interp *childInterp, Tcl_Interp *parentInterp,
- Tcl_Obj *namePtr, Tcl_Obj *targetPtr, Tcl_Size objc,
- Tcl_Obj *const objv[]);
static int AliasDelete(Tcl_Interp *interp,
Tcl_Interp *childInterp, Tcl_Obj *namePtr);
static int AliasDescribe(Tcl_Interp *interp,
@@ -701,7 +697,7 @@ NRInterpCmd(
return TCL_ERROR;
}
- return AliasCreate(interp, childInterp, parentInterp, objv[3],
+ return TclAliasCreate(interp, childInterp, parentInterp, objv[3],
objv[5], objc - 6, objv + 6);
}
@@ -1232,7 +1228,7 @@ Tcl_CreateAlias(
targetObjPtr = Tcl_NewStringObj(targetCmd, -1);
Tcl_IncrRefCount(targetObjPtr);
- result = AliasCreate(childInterp, childInterp, targetInterp, childObjPtr,
+ result = TclAliasCreate(childInterp, childInterp, targetInterp, childObjPtr,
targetObjPtr, argc, objv);
for (i = 0; i < argc; i++) {
@@ -1279,7 +1275,7 @@ Tcl_CreateAliasObj(
targetObjPtr = Tcl_NewStringObj(targetCmd, -1);
Tcl_IncrRefCount(targetObjPtr);
- result = AliasCreate(childInterp, childInterp, targetInterp, childObjPtr,
+ result = TclAliasCreate(childInterp, childInterp, targetInterp, childObjPtr,
targetObjPtr, objc, objv);
Tcl_DecrRefCount(childObjPtr);
@@ -1452,7 +1448,7 @@ TclPreventAliasLoop(
/*
*----------------------------------------------------------------------
*
- * AliasCreate --
+ * TclAliasCreate --
*
* Helper function to do the work to actually create an alias.
*
@@ -1466,8 +1462,8 @@ TclPreventAliasLoop(
*----------------------------------------------------------------------
*/
-static int
-AliasCreate(
+int
+TclAliasCreate(
Tcl_Interp *interp, /* Interp for error reporting. */
Tcl_Interp *childInterp, /* Interp where alias cmd will live or from
* which alias will be deleted. */
@@ -2468,7 +2464,7 @@ ChildCreate(
TclNewLiteralStringObj(clockObj, "clock");
Tcl_IncrRefCount(clockObj);
- status = AliasCreate(interp, childInterp, parentInterp, clockObj,
+ status = TclAliasCreate(interp, childInterp, parentInterp, clockObj,
clockObj, 0, NULL);
Tcl_DecrRefCount(clockObj);
if (status != TCL_OK) {
@@ -2558,7 +2554,7 @@ NRChildCmd(
return AliasDelete(interp, childInterp, objv[2]);
}
} else {
- return AliasCreate(interp, childInterp, interp, objv[2],
+ return TclAliasCreate(interp, childInterp, interp, objv[2],
objv[3], objc - 4, objv + 4);
}
}
diff --git a/generic/tclOO.c b/generic/tclOO.c
index e1dd40f..1fa9470 100644
--- a/generic/tclOO.c
+++ b/generic/tclOO.c
@@ -17,38 +17,66 @@
#include "tclOOInt.h"
/*
+ * Commands in oo and oo::Helpers.
+ */
+
+static const struct StdCommands {
+ const char *name;
+ Tcl_ObjCmdProc *objProc;
+ Tcl_ObjCmdProc *nreProc;
+ CompileProc *compileProc;
+ int flags;
+} ooCmds[] = {
+ {"define", TclOODefineObjCmd, NULL, NULL, 0},
+ {"objdefine", TclOOObjDefObjCmd, NULL, NULL, 0},
+ {"copy", TclOOCopyObjectCmd, NULL, NULL, 0},
+ {"DelegateName", TclOODelegateNameObjCmd, NULL, NULL, 0},
+ {NULL, NULL, NULL, NULL, 0}
+}, helpCmds[] = {
+ {"callback", TclOOCallbackObjCmd, NULL, NULL, 0},
+ {"mymethod", TclOOCallbackObjCmd, NULL, NULL, 0},
+ {"classvariable", TclOOClassVariableObjCmd, NULL, NULL, 0},
+ {"link", TclOOLinkObjCmd, NULL, NULL, 0},
+ {"next", NULL, TclOONextObjCmd, TclCompileObjectNextCmd, CMD_COMPILES_EXPANDED},
+ {"nextto", NULL, TclOONextToObjCmd, TclCompileObjectNextToCmd, CMD_COMPILES_EXPANDED},
+ {"self", TclOOSelfObjCmd, NULL, TclCompileObjectSelfCmd, 0},
+ {NULL, NULL, NULL, NULL, 0}
+};
+
+/*
* Commands in oo::define and oo::objdefine.
*/
-static const struct {
+static const struct DefineCommands {
const char *name;
Tcl_ObjCmdProc *objProc;
int flag;
} defineCmds[] = {
- {"constructor", TclOODefineConstructorObjCmd, 0},
+ {"classmethod", TclOODefineClassMethodObjCmd, 0},
+ {"constructor", TclOODefineConstructorObjCmd, 0},
{"definitionnamespace", TclOODefineDefnNsObjCmd, 0},
- {"deletemethod", TclOODefineDeleteMethodObjCmd, 0},
- {"destructor", TclOODefineDestructorObjCmd, 0},
- {"export", TclOODefineExportObjCmd, 0},
- {"forward", TclOODefineForwardObjCmd, 0},
- {"initialise", TclOODefineInitialiseObjCmd, 0},
- {"initialize", TclOODefineInitialiseObjCmd, 0},
- {"method", TclOODefineMethodObjCmd, 0},
- {"private", TclOODefinePrivateObjCmd, 0},
- {"renamemethod", TclOODefineRenameMethodObjCmd, 0},
- {"self", TclOODefineSelfObjCmd, 0},
- {"unexport", TclOODefineUnexportObjCmd, 0},
+ {"deletemethod", TclOODefineDeleteMethodObjCmd, 0},
+ {"destructor", TclOODefineDestructorObjCmd, 0},
+ {"export", TclOODefineExportObjCmd, 0},
+ {"forward", TclOODefineForwardObjCmd, 0},
+ {"initialise", TclOODefineInitialiseObjCmd, 0},
+ {"initialize", TclOODefineInitialiseObjCmd, 0},
+ {"method", TclOODefineMethodObjCmd, 0},
+ {"private", TclOODefinePrivateObjCmd, 0},
+ {"renamemethod", TclOODefineRenameMethodObjCmd, 0},
+ {"self", TclOODefineSelfObjCmd, 0},
+ {"unexport", TclOODefineUnexportObjCmd, 0},
{NULL, NULL, 0}
}, objdefCmds[] = {
- {"class", TclOODefineClassObjCmd, 1},
- {"deletemethod", TclOODefineDeleteMethodObjCmd, 1},
- {"export", TclOODefineExportObjCmd, 1},
- {"forward", TclOODefineForwardObjCmd, 1},
- {"method", TclOODefineMethodObjCmd, 1},
- {"private", TclOODefinePrivateObjCmd, 1},
- {"renamemethod", TclOODefineRenameMethodObjCmd, 1},
- {"self", TclOODefineObjSelfObjCmd, 0},
- {"unexport", TclOODefineUnexportObjCmd, 1},
+ {"class", TclOODefineClassObjCmd, 1},
+ {"deletemethod", TclOODefineDeleteMethodObjCmd, 1},
+ {"export", TclOODefineExportObjCmd, 1},
+ {"forward", TclOODefineForwardObjCmd, 1},
+ {"method", TclOODefineMethodObjCmd, 1},
+ {"private", TclOODefinePrivateObjCmd, 1},
+ {"renamemethod", TclOODefineRenameMethodObjCmd, 1},
+ {"self", TclOODefineObjSelfObjCmd, 0},
+ {"unexport", TclOODefineUnexportObjCmd, 1},
{NULL, NULL, 0}
};
@@ -69,7 +97,7 @@ static int CloneClassMethod(Tcl_Interp *interp, Class *clsPtr,
Method **newMPtrPtr);
static int CloneObjectMethod(Tcl_Interp *interp, Object *oPtr,
Method *mPtr, Tcl_Obj *namePtr);
-static void DeletedHelpersNamespace(void *clientData);
+static Tcl_NamespaceDeleteProc DeletedHelpersNamespace;
static Tcl_NRPostProc FinalizeAlloc;
static Tcl_NRPostProc FinalizeNext;
static Tcl_NRPostProc FinalizeObjectCall;
@@ -78,23 +106,17 @@ static void InitClassSystemRoots(Tcl_Interp *interp,
Foundation *fPtr);
static int InitFoundation(Tcl_Interp *interp);
static Tcl_InterpDeleteProc KillFoundation;
-static void MyDeleted(void *clientData);
-static void ObjectNamespaceDeleted(void *clientData);
+static Tcl_CmdDeleteProc MyDeleted;
+static Tcl_NamespaceDeleteProc ObjectNamespaceDeleted;
static Tcl_CommandTraceProc ObjectRenamedTrace;
static inline void RemoveClass(Class **list, size_t num, size_t idx);
static inline void RemoveObject(Object **list, size_t num, size_t idx);
static inline void SquelchCachedName(Object *oPtr);
-static int PublicNRObjectCmd(void *clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const *objv);
-static int PrivateNRObjectCmd(void *clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const *objv);
-static int MyClassNRObjCmd(void *clientData,
- Tcl_Interp *interp, int objc,
- Tcl_Obj *const *objv);
-static void MyClassDeleted(void *clientData);
+static Tcl_ObjCmdProc PublicNRObjectCmd;
+static Tcl_ObjCmdProc PrivateNRObjectCmd;
+static Tcl_ObjCmdProc MyClassNRObjCmd;
+static Tcl_CmdDeleteProc MyClassDeleted;
/*
* Methods in the oo::object and oo::class classes. First, we define a helper
@@ -145,8 +167,9 @@ static const char initScript[] =
"package ifneeded TclOO " TCLOO_PATCHLEVEL " {# Already present, OK?};"
#endif
"package ifneeded tcl::oo " TCLOO_PATCHLEVEL " {# Already present, OK?};"
-"namespace eval ::oo { variable version " TCLOO_VERSION " };"
-"namespace eval ::oo { variable patchlevel " TCLOO_PATCHLEVEL " };";
+"namespace eval ::oo {"
+" variable version " TCLOO_VERSION " patchlevel " TCLOO_PATCHLEVEL
+"};";
/* "tcl_findLibrary tcloo $oo::version $oo::version" */
/* " tcloo.tcl OO_LIBRARY oo::library;"; */
@@ -370,14 +393,20 @@ InitFoundation(
TclNewLiteralStringObj(fPtr->clonedName, "<cloned>");
TclNewLiteralStringObj(fPtr->defineName, "::oo::define");
TclNewLiteralStringObj(fPtr->myName, "my");
- TclNewLiteralStringObj(fPtr->mcdName, "::oo::MixinClassDelegates");
+ TclNewLiteralStringObj(fPtr->slotGetName, "Get");
+ TclNewLiteralStringObj(fPtr->slotSetName, "Set");
+ TclNewLiteralStringObj(fPtr->slotResolveName, "Resolve");
+ TclNewLiteralStringObj(fPtr->slotDefOpName, "--default-operation");
Tcl_IncrRefCount(fPtr->unknownMethodNameObj);
Tcl_IncrRefCount(fPtr->constructorName);
Tcl_IncrRefCount(fPtr->destructorName);
Tcl_IncrRefCount(fPtr->clonedName);
Tcl_IncrRefCount(fPtr->defineName);
Tcl_IncrRefCount(fPtr->myName);
- Tcl_IncrRefCount(fPtr->mcdName);
+ Tcl_IncrRefCount(fPtr->slotGetName);
+ Tcl_IncrRefCount(fPtr->slotSetName);
+ Tcl_IncrRefCount(fPtr->slotResolveName);
+ Tcl_IncrRefCount(fPtr->slotDefOpName);
TclCreateObjCommandInNs(interp, "UnknownDefinition", fPtr->ooNs,
TclOOUnknownDefinition, NULL, NULL);
@@ -432,20 +461,16 @@ InitFoundation(
* ensemble.
*/
- CreateCmdInNS(interp, fPtr->helpersNs, "callback",
- TclOOCallbackObjCmd, NULL, NULL, 0);
- CreateCmdInNS(interp, fPtr->helpersNs, "mymethod",
- TclOOCallbackObjCmd, NULL, NULL, 0);
- CreateCmdInNS(interp, fPtr->helpersNs, "next",
- NULL, TclOONextObjCmd, TclCompileObjectNextCmd, CMD_COMPILES_EXPANDED);
- CreateCmdInNS(interp, fPtr->helpersNs, "nextto",
- NULL, TclOONextToObjCmd, TclCompileObjectNextToCmd, CMD_COMPILES_EXPANDED);
- CreateCmdInNS(interp, fPtr->helpersNs, "self",
- TclOOSelfObjCmd, NULL, TclCompileObjectSelfCmd, 0);
-
- CreateCmdInNS(interp, fPtr->ooNs, "define", TclOODefineObjCmd, NULL, NULL, 0);
- CreateCmdInNS(interp, fPtr->ooNs, "objdefine", TclOOObjDefObjCmd, NULL, NULL, 0);
- CreateCmdInNS(interp, fPtr->ooNs, "copy", TclOOCopyObjectCmd, NULL, NULL, 0);
+ for (i = 0 ; helpCmds[i].name ; i++) {
+ CreateCmdInNS(interp, fPtr->helpersNs, helpCmds[i].name,
+ helpCmds[i].objProc, helpCmds[i].nreProc,
+ helpCmds[i].compileProc, helpCmds[i].flags);
+ }
+ for (i = 0 ; ooCmds[i].name ; i++) {
+ CreateCmdInNS(interp, fPtr->ooNs, ooCmds[i].name,
+ ooCmds[i].objProc, ooCmds[i].nreProc,
+ ooCmds[i].compileProc, ooCmds[i].flags);
+ }
TclOOInitInfo(interp);
@@ -620,7 +645,10 @@ KillFoundation(
TclDecrRefCount(fPtr->clonedName);
TclDecrRefCount(fPtr->defineName);
TclDecrRefCount(fPtr->myName);
- TclDecrRefCount(fPtr->mcdName);
+ TclDecrRefCount(fPtr->slotGetName);
+ TclDecrRefCount(fPtr->slotSetName);
+ TclDecrRefCount(fPtr->slotResolveName);
+ TclDecrRefCount(fPtr->slotDefOpName);
TclOODecrRefCount(fPtr->objectCls->thisPtr);
TclOODecrRefCount(fPtr->classCls->thisPtr);
@@ -804,6 +832,7 @@ AllocObject(
oPtr->myclassCommand = TclNRCreateCommandInNs(interp, "myclass",
oPtr->namespacePtr, TclOOMyClassObjCmd, MyClassNRObjCmd, oPtr,
MyClassDeleted);
+ oPtr->linkedCmdsList = NULL;
return oPtr;
}
@@ -848,6 +877,17 @@ MyDeleted(
{
Object *oPtr = (Object *) clientData;
+ if (oPtr->linkedCmdsList) {
+ Tcl_Size linkc, i;
+ Tcl_Obj **linkv;
+ TclListObjGetElements(NULL, oPtr->linkedCmdsList, &linkc, &linkv);
+ for (i=0 ; i<linkc ; i++) {
+ Tcl_Obj *link = linkv[i];
+ (void) Tcl_DeleteCommand(oPtr->fPtr->interp, TclGetString(link));
+ }
+ Tcl_DecrRefCount(oPtr->linkedCmdsList);
+ oPtr->linkedCmdsList = NULL;
+ }
oPtr->myCommand = NULL;
}
@@ -1248,7 +1288,7 @@ ObjectNamespaceDeleted(
* freed memory.
*/
- if (((Command *) oPtr->command)->flags && CMD_DYING) {
+ if (((Command *) oPtr->command)->flags & CMD_DYING) {
/*
* Something has already started the command deletion process. We can
* go ahead and clean up the namespace,
diff --git a/generic/tclOOBasic.c b/generic/tclOOBasic.c
index aefa91d..f72529f 100644
--- a/generic/tclOOBasic.c
+++ b/generic/tclOOBasic.c
@@ -19,7 +19,7 @@
static inline Tcl_Object *AddConstructionFinalizer(Tcl_Interp *interp);
static Tcl_NRPostProc AfterNRDestructor;
-static Tcl_NRPostProc DecrRefsPostClassConstructor;
+static Tcl_NRPostProc PostClassConstructor;
static Tcl_NRPostProc FinalizeConstruction;
static Tcl_NRPostProc FinalizeEval;
static Tcl_NRPostProc NextRestoreFrame;
@@ -68,6 +68,119 @@ FinalizeConstruction(
/*
* ----------------------------------------------------------------------
*
+ * MixinClassDelegates --
+ *
+ * Internal utility for setting up the class delegate.
+ * Runs after the class has called [oo::define] on its argument.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+// Look up the delegate for a class.
+static inline Class *
+GetClassDelegate(
+ Tcl_Interp *interp,
+ Class *clsPtr)
+{
+ Tcl_Obj *delegateName = Tcl_ObjPrintf("%s:: oo ::delegate",
+ clsPtr->thisPtr->namespacePtr->fullName);
+ Class *delegatePtr = TclOOGetClassFromObj(interp, delegateName);
+ Tcl_DecrRefCount(delegateName);
+ return delegatePtr;
+}
+
+/*
+ * Patches in the appropriate class delegates' superclasses.
+ * Sonewhat messy because the list of superclasses isn't modified frequently.
+ */
+static inline void
+SetDelegateSuperclasses(
+ Tcl_Interp *interp,
+ Class *clsPtr,
+ Class *delegatePtr)
+{
+ // Build new list of superclasses
+ int i, j = delegatePtr->superclasses.num, k;
+ Class *superPtr, **supers = (Class **) Tcl_Alloc(sizeof(Class *) *
+ (delegatePtr->superclasses.num + clsPtr->superclasses.num));
+ if (delegatePtr->superclasses.num) {
+ memcpy(supers, delegatePtr->superclasses.list,
+ sizeof(Class *) * delegatePtr->superclasses.num);
+ }
+ FOREACH(superPtr, clsPtr->superclasses) {
+ Class *superDelegatePtr = GetClassDelegate(interp, superPtr);
+ if (!superDelegatePtr) {
+ continue;
+ }
+ for (k=0 ; k<=j ; k++) {
+ if (k == j) {
+ supers[j++] = superDelegatePtr;
+ TclOOAddToSubclasses(delegatePtr, superDelegatePtr);
+ AddRef(superDelegatePtr->thisPtr);
+ break;
+ } else if (supers[k] == superDelegatePtr) {
+ break;
+ }
+ }
+ }
+
+ // Install new list of superclasses;
+ if (delegatePtr->superclasses.num) {
+ Tcl_Free(delegatePtr->superclasses.list);
+ }
+ delegatePtr->superclasses.list = supers;
+ delegatePtr->superclasses.num = j;
+
+ // Definitely don't need to bump any epoch here
+}
+
+/*
+ * Mixes the delegate into its controlling class.
+ */
+static inline void
+InstallDelegateAsMixin(
+ Tcl_Interp *interp,
+ Class *clsPtr,
+ Class *delegatePtr)
+{
+ if (clsPtr->thisPtr->mixins.num == 0) {
+ TclOOObjectSetMixins(clsPtr->thisPtr, 1, &delegatePtr);
+ return;
+ }
+ Class **mixins = (Class **) TclStackAlloc(interp,
+ sizeof(Class *) * (clsPtr->thisPtr->mixins.num + 1));
+ for (int i = 0; i < clsPtr->thisPtr->mixins.num; i++) {
+ mixins[i] = clsPtr->thisPtr->mixins.list[i];
+ if (mixins[i] == delegatePtr) {
+ TclStackFree(interp, (void *) mixins);
+ return;
+ }
+ }
+ mixins[clsPtr->thisPtr->mixins.num] = delegatePtr;
+ TclOOObjectSetMixins(clsPtr->thisPtr, clsPtr->thisPtr->mixins.num + 1, mixins);
+ TclStackFree(interp, mixins);
+}
+
+// Patches in the appropriate class delegates.
+static void
+MixinClassDelegates(
+ Tcl_Interp *interp,
+ Object *oPtr,
+ Tcl_Obj *delegateName)
+{
+ Class *clsPtr = oPtr->classPtr;
+ if (clsPtr) {
+ Class *delegatePtr = TclOOGetClassFromObj(interp, delegateName);
+ if (delegatePtr) {
+ SetDelegateSuperclasses(interp, clsPtr, delegatePtr);
+ InstallDelegateAsMixin(interp, clsPtr, delegatePtr);
+ }
+ }
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* TclOO_Class_Constructor --
*
* Implementation for oo::class constructor.
@@ -84,7 +197,6 @@ TclOO_Class_Constructor(
Tcl_Obj *const *objv)
{
Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
- Tcl_Obj **invoke, *nameObj;
size_t skip = Tcl_ObjectContextSkippedArgs(context);
if ((size_t) objc > skip + 1) {
@@ -101,25 +213,28 @@ TclOO_Class_Constructor(
* argument to [oo::define]. [Bug 680503]
*/
- nameObj = Tcl_ObjPrintf("%s:: oo ::delegate",
+ Tcl_Obj *delegateName = Tcl_ObjPrintf("%s:: oo ::delegate",
oPtr->namespacePtr->fullName);
+ Tcl_IncrRefCount(delegateName);
Tcl_NewObjectInstance(interp, (Tcl_Class) oPtr->fPtr->classCls,
- TclGetString(nameObj), NULL, TCL_INDEX_NONE, NULL, 0);
- Tcl_BounceRefCount(nameObj);
+ TclGetString(delegateName), NULL, TCL_INDEX_NONE, NULL, 0);
/*
* If there's nothing else to do, we're done.
*/
if ((size_t) objc == skip) {
- return TCL_OK;
+ Tcl_InterpState saved = Tcl_SaveInterpState(interp, TCL_OK);
+ MixinClassDelegates(interp, oPtr, delegateName);
+ Tcl_DecrRefCount(delegateName);
+ return Tcl_RestoreInterpState(interp, saved);
}
/*
* Delegate to [oo::define] to do the work.
*/
- invoke = (Tcl_Obj **) TclStackAlloc(interp, 3 * sizeof(Tcl_Obj *));
+ Tcl_Obj **invoke = (Tcl_Obj **) TclStackAlloc(interp, 3 * sizeof(Tcl_Obj *));
invoke[0] = oPtr->fPtr->defineName;
invoke[1] = TclOOObjectName(interp, oPtr);
invoke[2] = objv[objc - 1];
@@ -132,8 +247,8 @@ TclOO_Class_Constructor(
Tcl_IncrRefCount(invoke[0]);
Tcl_IncrRefCount(invoke[1]);
Tcl_IncrRefCount(invoke[2]);
- TclNRAddCallback(interp, DecrRefsPostClassConstructor,
- invoke, oPtr, NULL, NULL);
+ TclNRAddCallback(interp, PostClassConstructor,
+ invoke, oPtr, delegateName, NULL);
/*
* Tricky point: do not want the extra reported level in the Tcl stack
@@ -143,33 +258,28 @@ TclOO_Class_Constructor(
return TclNREvalObjv(interp, 3, invoke, TCL_EVAL_NOERR, NULL);
}
+/*
+ * Called *after* [oo::define] inside the constructor of a class.
+ * Cleans up some temporary storage and sets up the delegate.
+ */
static int
-DecrRefsPostClassConstructor(
+PostClassConstructor(
void *data[],
Tcl_Interp *interp,
int result)
{
Tcl_Obj **invoke = (Tcl_Obj **) data[0];
Object *oPtr = (Object *) data[1];
- Tcl_InterpState saved;
- int code;
+ Tcl_Obj *delegateName = (Tcl_Obj *) data[2];
TclDecrRefCount(invoke[0]);
TclDecrRefCount(invoke[1]);
TclDecrRefCount(invoke[2]);
- invoke[0] = oPtr->fPtr->mcdName;
- invoke[1] = TclOOObjectName(interp, oPtr);
- Tcl_IncrRefCount(invoke[0]);
- Tcl_IncrRefCount(invoke[1]);
- saved = Tcl_SaveInterpState(interp, result);
- code = Tcl_EvalObjv(interp, 2, invoke, 0);
- TclDecrRefCount(invoke[0]);
- TclDecrRefCount(invoke[1]);
TclStackFree(interp, invoke);
- if (code != TCL_OK) {
- Tcl_DiscardInterpState(saved);
- return code;
- }
+
+ Tcl_InterpState saved = Tcl_SaveInterpState(interp, result);
+ MixinClassDelegates(interp, oPtr, delegateName);
+ Tcl_DecrRefCount(delegateName);
return Tcl_RestoreInterpState(interp, saved);
}
@@ -904,6 +1014,98 @@ TclOO_Object_VarName(
/*
* ----------------------------------------------------------------------
*
+ * TclOOLinkObjCmd --
+ *
+ * Implementation of the [link] command, that makes a command that
+ * invokes a method on the current object. The name of the command and
+ * the name of the method match by default. Note that this command is
+ * only ever to be used inside the body of a procedure-like method,
+ * and is typically intended for constructors.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+TclOOLinkObjCmd(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ // Set up common bits.
+ CallFrame *framePtr = ((Interp *) interp)->varFramePtr;
+ 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])));
+ OO_ERROR(interp, CONTEXT_REQUIRED);
+ return TCL_ERROR;
+ }
+ CallContext *context = (CallContext *) framePtr->clientData;
+ Object *oPtr = context->oPtr;
+ if (!oPtr->myCommand) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "cannot link to non-existent callback handle"));
+ OO_ERROR(interp, MY_GONE);
+ return TCL_ERROR;
+ }
+ Tcl_Obj *myCmd = Tcl_NewObj();
+ Tcl_GetCommandFullName(interp, oPtr->myCommand, myCmd);
+ if (!oPtr->linkedCmdsList) {
+ oPtr->linkedCmdsList = Tcl_NewListObj(0, NULL);
+ Tcl_IncrRefCount(oPtr->linkedCmdsList);
+ }
+
+ // For each argument
+ for (int i=1; i<objc; i++) {
+ Tcl_Size linkc;
+ Tcl_Obj **linkv, *src, *dst;
+
+ // Parse as list of (one or) two items: source and destination names
+ if (TclListObjGetElements(interp, objv[i], &linkc, &linkv) != TCL_OK) {
+ Tcl_BounceRefCount(myCmd);
+ return TCL_ERROR;
+ }
+ switch (linkc) {
+ case 1:
+ // Degenerate case
+ src = dst = linkv[0];
+ break;
+ case 2:
+ src = linkv[0];
+ dst = linkv[1];
+ break;
+ default:
+ Tcl_BounceRefCount(myCmd);
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad link description; must only have one or two elements"));
+ OO_ERROR(interp, CMDLINK_FORMAT);
+ return TCL_ERROR;
+ }
+
+ // Qualify the source if necessary
+ const char *srcStr = TclGetString(src);
+ if (srcStr[0] != ':' || srcStr[1] != ':') {
+ src = Tcl_ObjPrintf("%s::%s",
+ context->oPtr->namespacePtr->fullName, srcStr);
+ }
+
+ // Make the alias command
+ if (TclAliasCreate(interp, interp, interp, src, myCmd, 1, &dst) != TCL_OK) {
+ Tcl_BounceRefCount(myCmd);
+ Tcl_BounceRefCount(src);
+ return TCL_ERROR;
+ }
+
+ // Remember the alias for cleanup if necessary
+ Tcl_ListObjAppendElement(NULL, oPtr->linkedCmdsList, src);
+ }
+ Tcl_BounceRefCount(myCmd);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* TclOONextObjCmd, TclOONextToObjCmd --
*
* Implementation of the [next] and [nextto] commands. Note that these
@@ -1412,6 +1614,132 @@ TclOOCallbackObjCmd(
}
/*
+ * ----------------------------------------------------------------------
+ *
+ * TclOOClassVariableObjCmd --
+ *
+ * Implementation of the [classvariable] command, which links to
+ * variables in the class of the current object.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+TclOOClassVariableObjCmd(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Interp *iPtr = (Interp *) interp;
+ CallFrame *framePtr = iPtr->varFramePtr;
+
+ if (objc < 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "name ...");
+ return TCL_ERROR;
+ }
+
+ /*
+ * 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])));
+ OO_ERROR(interp, CONTEXT_REQUIRED);
+ return TCL_ERROR;
+ }
+
+ // Get a reference to the class's namespace
+ CallContext *contextPtr = (CallContext *) framePtr->clientData;
+ Class *clsPtr = CurrentlyInvoked(contextPtr).mPtr->declaringClassPtr;
+ if (clsPtr == NULL) {
+ Tcl_SetObjResult(interp, Tcl_NewStringObj(
+ "method not defined by a class", TCL_AUTO_LENGTH));
+ OO_ERROR(interp, UNMATCHED_CONTEXT);
+ return TCL_ERROR;
+ }
+ Tcl_Namespace *clsNsPtr = clsPtr->thisPtr->namespacePtr;
+
+ // Check the list of variable names
+ for (int i = 1; i < objc; i++) {
+ const char *varName = TclGetString(objv[i]);
+ if (Tcl_StringMatch(varName, "*(*)")) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad variable name \"%s\": can't create a %s",
+ varName, "scalar variable that looks like an array element"));
+ Tcl_SetErrorCode(interp, "TCL", "UPVAR", "LOCAL_ELEMENT", NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_StringMatch(varName, "*::*")) {
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf(
+ "bad variable name \"%s\": can't create a %s",
+ varName, "local variable with a namespace separator in it"));
+ Tcl_SetErrorCode(interp, "TCL", "UPVAR", "LOCAL_ELEMENT", NULL);
+ return TCL_ERROR;
+ }
+ }
+
+ // Lastly, link the caller's local variables to the class's variables
+ Tcl_Namespace *ourNsPtr = (Tcl_Namespace *) iPtr->varFramePtr->nsPtr;
+ for (int i = 1; i < objc; i++) {
+ // Locate the other variable.
+ iPtr->varFramePtr->nsPtr = (Namespace *) clsNsPtr;
+ Var *arrayPtr, *otherPtr = TclObjLookupVarEx(interp, objv[i], NULL,
+ (TCL_NAMESPACE_ONLY|TCL_LEAVE_ERR_MSG|TCL_AVOID_RESOLVERS),
+ "access", /*createPart1*/ 1, /*createPart2*/ 0, &arrayPtr);
+ iPtr->varFramePtr->nsPtr = (Namespace *) ourNsPtr;
+ if (otherPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ // Create the new variable and link it to otherPtr.
+ if (TclPtrObjMakeUpvarIdx(interp, otherPtr, objv[i], 0,
+ TCL_INDEX_NONE) != TCL_OK) {
+ return TCL_ERROR;
+ }
+ }
+
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TclOODelegateNameObjCmd --
+ *
+ * Implementation of the [oo::DelegateName] command, which is a utility
+ * that gets the name of the class delegate for a class. It's trivial,
+ * but makes working with them much easier as delegate names are
+ * intentionally hard to create by accident.
+ *
+ * Not part of TclOO public API. No public documentation.
+ *
+ * ----------------------------------------------------------------------
+ */
+int
+TclOODelegateNameObjCmd(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "class");
+ return TCL_ERROR;
+ }
+ Class *clsPtr = TclOOGetClassFromObj(interp, objv[1]);
+ if (clsPtr == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, Tcl_ObjPrintf("%s:: oo ::delegate",
+ clsPtr->thisPtr->namespacePtr->fullName));
+ return TCL_OK;
+}
+
+/*
* Local Variables:
* mode: c
* c-basic-offset: 4
diff --git a/generic/tclOODefineCmds.c b/generic/tclOODefineCmds.c
index 5ca69e2..e3fbe3f 100644
--- a/generic/tclOODefineCmds.c
+++ b/generic/tclOODefineCmds.c
@@ -37,16 +37,27 @@ typedef struct DeclaredSlot {
const Tcl_MethodType getterType;
const Tcl_MethodType setterType;
const Tcl_MethodType resolverType;
+ const char *defaultOp; // The default op, if not set by the class
} DeclaredSlot;
-#define SLOT(name,getter,setter,resolver) \
+#define SLOT(name,getter,setter,resolver,defOp) \
{"::oo::" name, \
- {TCL_OO_METHOD_VERSION_CURRENT, "core method: " name " Getter", \
+ {TCL_OO_METHOD_VERSION_1, "core method: " name " Getter", \
getter, NULL, NULL}, \
- {TCL_OO_METHOD_VERSION_CURRENT, "core method: " name " Setter", \
+ {TCL_OO_METHOD_VERSION_1, "core method: " name " Setter", \
setter, NULL, NULL}, \
- {TCL_OO_METHOD_VERSION_CURRENT, "core method: " name " Resolver", \
- resolver, NULL, NULL}}
+ {TCL_OO_METHOD_VERSION_1, "core method: " name " Resolver", \
+ resolver, NULL, NULL}, (defOp)}
+
+typedef struct DeclaredSlotMethod {
+ const char *name;
+ int flags;
+ const Tcl_MethodType implType;
+} DeclaredSlotMethod;
+
+#define SLOT_METHOD(name,impl,flags) \
+ {name, flags, {TCL_OO_METHOD_VERSION_1, \
+ "core method: " name " slot", impl, NULL, NULL}}
/*
* A [string match] pattern used to determine if a method should be exported.
@@ -78,6 +89,33 @@ static inline void RecomputeClassCacheFlag(Object *oPtr);
static int RenameDeleteMethod(Tcl_Interp *interp, Object *oPtr,
int useClass, Tcl_Obj *const fromPtr,
Tcl_Obj *const toPtr);
+static int Slot_Append(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_AppendNew(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_Clear(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_Prepend(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_Remove(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_Resolve(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_Set(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
+static int Slot_Unimplemented(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext,
+ int, Tcl_Obj *const *);
+static int Slot_Unknown(void *,
+ Tcl_Interp *interp, Tcl_ObjectContext context,
+ int objc, Tcl_Obj *const *objv);
static int ClassFilter_Get(void *clientData,
Tcl_Interp *interp, Tcl_ObjectContext context,
int objc, Tcl_Obj *const *objv);
@@ -153,26 +191,40 @@ static int ResolveClass(void *clientData,
*/
static const DeclaredSlot slots[] = {
- SLOT("define::filter", ClassFilter_Get, ClassFilter_Set, NULL),
- SLOT("define::mixin", ClassMixin_Get, ClassMixin_Set, ResolveClass),
- SLOT("define::superclass", ClassSuper_Get, ClassSuper_Set, ResolveClass),
- SLOT("define::variable", ClassVars_Get, ClassVars_Set, NULL),
- SLOT("objdefine::filter", ObjFilter_Get, ObjFilter_Set, NULL),
- SLOT("objdefine::mixin", ObjMixin_Get, ObjMixin_Set, ResolveClass),
- SLOT("objdefine::variable", ObjVars_Get, ObjVars_Set, NULL),
+ SLOT("define::filter", ClassFilter_Get, ClassFilter_Set, NULL, NULL),
+ SLOT("define::mixin", ClassMixin_Get, ClassMixin_Set, ResolveClass, "-set"),
+ SLOT("define::superclass", ClassSuper_Get, ClassSuper_Set, ResolveClass, "-set"),
+ SLOT("define::variable", ClassVars_Get, ClassVars_Set, NULL, NULL),
+ SLOT("objdefine::filter", ObjFilter_Get, ObjFilter_Set, NULL, NULL),
+ SLOT("objdefine::mixin", ObjMixin_Get, ObjMixin_Set, ResolveClass, "-set"),
+ SLOT("objdefine::variable", ObjVars_Get, ObjVars_Set, NULL, NULL),
SLOT("configuresupport::readableproperties",
Configurable_ClassReadableProps_Get,
- Configurable_ClassReadableProps_Set, NULL),
+ Configurable_ClassReadableProps_Set, NULL, NULL),
SLOT("configuresupport::writableproperties",
Configurable_ClassWritableProps_Get,
- Configurable_ClassWritableProps_Set, NULL),
+ Configurable_ClassWritableProps_Set, NULL, NULL),
SLOT("configuresupport::objreadableproperties",
Configurable_ObjectReadableProps_Get,
- Configurable_ObjectReadableProps_Set, NULL),
+ Configurable_ObjectReadableProps_Set, NULL, NULL),
SLOT("configuresupport::objwritableproperties",
Configurable_ObjectWritableProps_Get,
- Configurable_ObjectWritableProps_Set, NULL),
- {NULL, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}}
+ Configurable_ObjectWritableProps_Set, NULL, NULL),
+ {NULL, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, {0, 0, 0, 0, 0}, 0}
+};
+
+static const DeclaredSlotMethod slotMethods[] = {
+ SLOT_METHOD("Get", Slot_Unimplemented, 0),
+ SLOT_METHOD("Resolve", Slot_Resolve, 0),
+ SLOT_METHOD("Set", Slot_Unimplemented, 0),
+ SLOT_METHOD("-append", Slot_Append, PUBLIC_METHOD),
+ SLOT_METHOD("-appendifnew", Slot_AppendNew, PUBLIC_METHOD),
+ SLOT_METHOD("-clear", Slot_Clear, PUBLIC_METHOD),
+ SLOT_METHOD("-prepend", Slot_Prepend, PUBLIC_METHOD),
+ SLOT_METHOD("-remove", Slot_Remove, PUBLIC_METHOD),
+ SLOT_METHOD("-set", Slot_Set, PUBLIC_METHOD),
+ SLOT_METHOD("unknown", Slot_Unknown, 0),
+ {NULL, 0, {0, 0, 0, 0, 0}}
};
/*
@@ -1179,6 +1231,119 @@ MagicDefinitionInvoke(
/*
* ----------------------------------------------------------------------
*
+ * ExportMethod, UnexportMethod, ExportInstanceMethod, UnexportInstanceMethod --
+ *
+ * Exporting and unexporting are done by setting or removing the
+ * PUBLIC_METHOD flag on the method record. If there is no such method in
+ * this class or object (i.e. the method comes from something inherited
+ * from or that we're an instance of) then we put in a blank record just
+ * to hold that flag (or its absence); such records are skipped over by
+ * the call chain engine *except* for their flags member.
+ *
+ * Caller has the responsibility to update any epochs if necessary.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+// Make a blank method record or look up the existing one.
+static inline Method *
+GetOrCreateMethod(
+ Tcl_HashTable *tablePtr,
+ Tcl_Obj *namePtr,
+ int *isNew)
+{
+ Tcl_HashEntry *hPtr = Tcl_CreateHashEntry(tablePtr, namePtr,
+ isNew);
+ if (*isNew) {
+ Method *mPtr = (Method *) Tcl_Alloc(sizeof(Method));
+ memset(mPtr, 0, sizeof(Method));
+ mPtr->refCount = 1;
+ mPtr->namePtr = namePtr;
+ Tcl_IncrRefCount(namePtr);
+ Tcl_SetHashValue(hPtr, mPtr);
+ return mPtr;
+ } else {
+ return (Method *) Tcl_GetHashValue(hPtr);
+ }
+}
+
+static int
+ExportMethod(
+ Class *clsPtr,
+ Tcl_Obj *namePtr)
+{
+ int isNew;
+ Method *mPtr = GetOrCreateMethod(&clsPtr->classMethods, namePtr, &isNew);
+ if (isNew || !(mPtr->flags & (PUBLIC_METHOD | PRIVATE_METHOD))) {
+ mPtr->flags |= PUBLIC_METHOD;
+ mPtr->flags &= ~TRUE_PRIVATE_METHOD;
+ isNew = 1;
+ }
+ return isNew;
+}
+
+static int
+UnexportMethod(
+ Class *clsPtr,
+ Tcl_Obj *namePtr)
+{
+ int isNew;
+ Method *mPtr = GetOrCreateMethod(&clsPtr->classMethods, namePtr, &isNew);
+ if (isNew || mPtr->flags & (PUBLIC_METHOD | TRUE_PRIVATE_METHOD)) {
+ mPtr->flags &= ~(PUBLIC_METHOD | TRUE_PRIVATE_METHOD);
+ isNew = 1;
+ }
+ return isNew;
+}
+
+// Make the table of methods in the instance if it doesn't already exist.
+static inline void
+InitMethodTable(
+ Object *oPtr)
+{
+ if (!oPtr->methodsPtr) {
+ oPtr->methodsPtr = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
+ Tcl_InitObjHashTable(oPtr->methodsPtr);
+ oPtr->flags &= ~USE_CLASS_CACHE;
+ }
+}
+
+static int
+ExportInstanceMethod(
+ Object *oPtr,
+ Tcl_Obj *namePtr)
+{
+ InitMethodTable(oPtr);
+
+ int isNew;
+ Method *mPtr = GetOrCreateMethod(oPtr->methodsPtr, namePtr, &isNew);
+ if (isNew || !(mPtr->flags & (PUBLIC_METHOD | PRIVATE_METHOD))) {
+ mPtr->flags |= PUBLIC_METHOD;
+ mPtr->flags &= ~TRUE_PRIVATE_METHOD;
+ isNew = 1;
+ }
+ return isNew;
+}
+
+static int
+UnexportInstanceMethod(
+ Object *oPtr,
+ Tcl_Obj *namePtr)
+{
+ InitMethodTable(oPtr);
+
+ int isNew;
+ Method *mPtr = GetOrCreateMethod(oPtr->methodsPtr, namePtr, &isNew);
+ if (isNew || mPtr->flags & (PUBLIC_METHOD | TRUE_PRIVATE_METHOD)) {
+ mPtr->flags &= ~(PUBLIC_METHOD | TRUE_PRIVATE_METHOD);
+ isNew = 1;
+ }
+ return isNew;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* TclOODefineObjCmd --
*
* Implementation of the "oo::define" command. Works by effectively doing
@@ -1887,22 +2052,18 @@ TclOODefineExportObjCmd(
Tcl_Obj *const *objv)
{
int isInstanceExport = (clientData != NULL);
- Object *oPtr;
- Method *mPtr;
- Tcl_HashEntry *hPtr;
- Class *clsPtr;
- int i, isNew, changed = 0;
+ int i, changed = 0;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "name ?name ...?");
return TCL_ERROR;
}
- oPtr = (Object *) TclOOGetDefineCmdContext(interp);
+ Object *oPtr = (Object *) TclOOGetDefineCmdContext(interp);
if (oPtr == NULL) {
return TCL_ERROR;
}
- clsPtr = oPtr->classPtr;
+ Class *clsPtr = oPtr->classPtr;
if (!isInstanceExport && !clsPtr) {
Tcl_SetObjResult(interp, Tcl_NewStringObj(
"attempt to misuse API", TCL_AUTO_LENGTH));
@@ -1921,33 +2082,9 @@ TclOODefineExportObjCmd(
*/
if (isInstanceExport) {
- if (!oPtr->methodsPtr) {
- oPtr->methodsPtr = (Tcl_HashTable *)
- Tcl_Alloc(sizeof(Tcl_HashTable));
- Tcl_InitObjHashTable(oPtr->methodsPtr);
- oPtr->flags &= ~USE_CLASS_CACHE;
- }
- hPtr = Tcl_CreateHashEntry(oPtr->methodsPtr, objv[i],
- &isNew);
- } else {
- hPtr = Tcl_CreateHashEntry(&clsPtr->classMethods, objv[i],
- &isNew);
- }
-
- if (isNew) {
- mPtr = (Method *) Tcl_Alloc(sizeof(Method));
- memset(mPtr, 0, sizeof(Method));
- mPtr->refCount = 1;
- mPtr->namePtr = objv[i];
- Tcl_IncrRefCount(objv[i]);
- Tcl_SetHashValue(hPtr, mPtr);
+ changed |= ExportInstanceMethod(oPtr, objv[i]);
} else {
- mPtr = (Method *) Tcl_GetHashValue(hPtr);
- }
- if (isNew || !(mPtr->flags & (PUBLIC_METHOD | PRIVATE_METHOD))) {
- mPtr->flags |= PUBLIC_METHOD;
- mPtr->flags &= ~TRUE_PRIVATE_METHOD;
- changed = 1;
+ changed |= ExportMethod(clsPtr, objv[i]);
}
}
@@ -2177,6 +2314,72 @@ TclOODefineMethodObjCmd(
/*
* ----------------------------------------------------------------------
*
+ * TclOODefineClassMethodObjCmd --
+ *
+ * Implementation of the "classmethod" subcommand of the "oo::define"
+ * command. Defines a class method. See define(n) for details.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+int
+TclOODefineClassMethodObjCmd(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ if (objc != 2 && objc != 4) {
+ Tcl_WrongNumArgs(interp, 1, objv, "name ?args body?");
+ return TCL_ERROR;
+ }
+ Class *clsPtr = TclOOGetClassDefineCmdContext(interp);
+ if (!clsPtr) {
+ return TCL_ERROR;
+ }
+
+ int isPublic = Tcl_StringMatch(TclGetString(objv[1]), PUBLIC_PATTERN)
+ ? PUBLIC_METHOD : 0;
+
+ // Create the method on the delegate class if the caller gave arguments and body
+ if (objc == 4) {
+ Tcl_Obj *delegateName = Tcl_ObjPrintf("%s:: oo ::delegate",
+ clsPtr->thisPtr->namespacePtr->fullName);
+ Class *delegatePtr = TclOOGetClassFromObj(interp, delegateName);
+ Tcl_DecrRefCount(delegateName);
+ if (!delegatePtr) {
+ return TCL_ERROR;
+ }
+ if (IsPrivateDefine(interp)) {
+ isPublic = 0;
+ }
+ if (TclOONewProcMethod(interp, delegatePtr, isPublic, objv[1],
+ objv[2], objv[3], NULL) == NULL) {
+ return TCL_ERROR;
+ }
+ }
+
+ // Make the connection to the delegate by forwarding
+ if (IsPrivateDefine(interp)) {
+ isPublic = TRUE_PRIVATE_METHOD;
+ }
+ Tcl_Obj *forwardArgs[] = {
+ Tcl_NewStringObj("myclass", -1),
+ objv[1]
+ };
+ Tcl_Obj *prefixObj = Tcl_NewListObj(2, forwardArgs);
+ Method *mPtr = TclOONewForwardMethod(interp, clsPtr, isPublic,
+ objv[1], prefixObj);
+ if (mPtr == NULL) {
+ Tcl_DecrRefCount(prefixObj);
+ return TCL_ERROR;
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* TclOODefineRenameMethodObjCmd --
*
* Implementation of the "renamemethod" subcommand of the "oo::define"
@@ -2251,10 +2454,8 @@ TclOODefineUnexportObjCmd(
{
int isInstanceUnexport = (clientData != NULL);
Object *oPtr;
- Method *mPtr;
- Tcl_HashEntry *hPtr;
Class *clsPtr;
- int i, isNew, changed = 0;
+ int i, changed = 0;
if (objc < 2) {
Tcl_WrongNumArgs(interp, 1, objv, "name ?name ...?");
@@ -2274,42 +2475,10 @@ TclOODefineUnexportObjCmd(
}
for (i = 1; i < objc; i++) {
- /*
- * Unexporting is done by removing the PUBLIC_METHOD flag from the
- * method record. If there is no such method in this object or class
- * (i.e. the method comes from something inherited from or that we're
- * an instance of) then we put in a blank record without that flag;
- * such records are skipped over by the call chain engine *except* for
- * their flags member.
- */
-
if (isInstanceUnexport) {
- if (!oPtr->methodsPtr) {
- oPtr->methodsPtr = (Tcl_HashTable *)
- Tcl_Alloc(sizeof(Tcl_HashTable));
- Tcl_InitObjHashTable(oPtr->methodsPtr);
- oPtr->flags &= ~USE_CLASS_CACHE;
- }
- hPtr = Tcl_CreateHashEntry(oPtr->methodsPtr, objv[i],
- &isNew);
- } else {
- hPtr = Tcl_CreateHashEntry(&clsPtr->classMethods, objv[i],
- &isNew);
- }
-
- if (isNew) {
- mPtr = (Method *) Tcl_Alloc(sizeof(Method));
- memset(mPtr, 0, sizeof(Method));
- mPtr->refCount = 1;
- mPtr->namePtr = objv[i];
- Tcl_IncrRefCount(objv[i]);
- Tcl_SetHashValue(hPtr, mPtr);
+ changed |= UnexportInstanceMethod(oPtr, objv[i]);
} else {
- mPtr = (Method *) Tcl_GetHashValue(hPtr);
- }
- if (isNew || mPtr->flags & (PUBLIC_METHOD | TRUE_PRIVATE_METHOD)) {
- mPtr->flags &= ~(PUBLIC_METHOD | TRUE_PRIVATE_METHOD);
- changed = 1;
+ changed |= UnexportMethod(clsPtr, objv[i]);
}
}
@@ -2387,8 +2556,9 @@ Tcl_ClassSetDestructor(
*
* TclOODefineSlots --
*
- * Create the "::oo::Slot" class and its standard instances. Class
- * definition is empty at the stage (added by scripting).
+ * Create the "::oo::Slot" class and its standard instances. These are
+ * basically lists at the low level of TclOO; this provides a more
+ * consistent interface to them.
*
* ----------------------------------------------------------------------
*/
@@ -2397,50 +2567,568 @@ int
TclOODefineSlots(
Foundation *fPtr)
{
- const DeclaredSlot *slotInfoPtr;
Tcl_Interp *interp = fPtr->interp;
- Tcl_Obj *getName, *setName, *resolveName;
Tcl_Object object = Tcl_NewObjectInstance(interp, (Tcl_Class)
fPtr->classCls, "::oo::Slot", NULL, TCL_INDEX_NONE, NULL, 0);
- Class *slotCls;
if (object == NULL) {
return TCL_ERROR;
}
- slotCls = ((Object *) object)->classPtr;
+ Tcl_Class slotCls = (Tcl_Class) ((Object *) object)->classPtr;
if (slotCls == NULL) {
return TCL_ERROR;
}
- TclNewLiteralStringObj(getName, "Get");
- TclNewLiteralStringObj(setName, "Set");
- TclNewLiteralStringObj(resolveName, "Resolve");
- for (slotInfoPtr = slots ; slotInfoPtr->name ; slotInfoPtr++) {
+ for (const DeclaredSlotMethod *smPtr = slotMethods; smPtr->name; smPtr++) {
+ Tcl_Obj *name = Tcl_NewStringObj(smPtr->name, -1);
+ Tcl_NewMethod(interp, slotCls, name, smPtr->flags,
+ &smPtr->implType, NULL);
+ Tcl_BounceRefCount(name);
+ }
+
+ // If a slot can't figure out what method to call directly, it uses
+ // --default-operation. That defaults to -append; we set that here.
+ Tcl_Obj *defaults[] = {
+ fPtr->myName,
+ Tcl_NewStringObj("-append", TCL_AUTO_LENGTH)
+ };
+ TclOONewForwardMethod(interp, (Class *) slotCls, 0,
+ fPtr->slotDefOpName, Tcl_NewListObj(2, defaults));
+
+ // Hide the destroy method. (We're definitely taking a ref to the name.)
+ UnexportMethod((Class *) slotCls,
+ Tcl_NewStringObj("destroy", TCL_AUTO_LENGTH));
+
+ for (const DeclaredSlot *slotPtr = slots ; slotPtr->name ; slotPtr++) {
Tcl_Object slotObject = Tcl_NewObjectInstance(interp,
- (Tcl_Class) slotCls, slotInfoPtr->name, NULL, TCL_INDEX_NONE,
- NULL, 0);
+ slotCls, slotPtr->name, NULL, TCL_INDEX_NONE, NULL, 0);
if (slotObject == NULL) {
continue;
}
- TclNewInstanceMethod(interp, slotObject, getName, 0,
- &slotInfoPtr->getterType, NULL);
- TclNewInstanceMethod(interp, slotObject, setName, 0,
- &slotInfoPtr->setterType, NULL);
- if (slotInfoPtr->resolverType.callProc) {
- TclNewInstanceMethod(interp, slotObject, resolveName, 0,
- &slotInfoPtr->resolverType, NULL);
+ TclNewInstanceMethod(interp, slotObject, fPtr->slotGetName, 0,
+ &slotPtr->getterType, NULL);
+ TclNewInstanceMethod(interp, slotObject, fPtr->slotSetName, 0,
+ &slotPtr->setterType, NULL);
+ if (slotPtr->resolverType.callProc) {
+ TclNewInstanceMethod(interp, slotObject, fPtr->slotResolveName, 0,
+ &slotPtr->resolverType, NULL);
+ }
+ if (slotPtr->defaultOp) {
+ Tcl_Obj *slotDefaults[] = {
+ fPtr->myName,
+ Tcl_NewStringObj(slotPtr->defaultOp, TCL_AUTO_LENGTH)
+ };
+ TclOONewForwardInstanceMethod(interp, (Object *) slotObject, 0,
+ fPtr->slotDefOpName, Tcl_NewListObj(2, slotDefaults));
}
}
- Tcl_BounceRefCount(getName);
- Tcl_BounceRefCount(setName);
- Tcl_BounceRefCount(resolveName);
return TCL_OK;
}
/*
* ----------------------------------------------------------------------
*
+ * CallSlotGet, CallSlotSet, CallSlotResolve, ResolveAll --
+ *
+ * How to call the standard low-level methods of a slot.
+ * ResolveAll is the lifting of CallSlotResolve to work over a whole
+ * list of items.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+// Call [$slot Get] to retrieve the list of contents of the slot
+static inline Tcl_Obj *
+CallSlotGet(
+ Tcl_Interp *interp,
+ Object *slot)
+{
+ Tcl_Obj *getArgs[] = {
+ slot->fPtr->myName,
+ slot->fPtr->slotGetName
+ };
+ int code = TclOOPrivateObjectCmd(slot, interp, 2, getArgs);
+ if (code != TCL_OK) {
+ return NULL;
+ }
+ return Tcl_GetObjResult(interp);
+}
+
+// Call [$slot Set $list] to set the list of contents of the slot
+static inline int
+CallSlotSet(
+ Tcl_Interp *interp,
+ Object *slot,
+ Tcl_Obj *list)
+{
+ Tcl_Obj *setArgs[] = {
+ slot->fPtr->myName,
+ slot->fPtr->slotSetName,
+ list
+ };
+ return TclOOPrivateObjectCmd(slot, interp, 3, setArgs);
+}
+
+// Call [$slot Resolve $item] to convert a slot item into canonical form
+static inline Tcl_Obj *
+CallSlotResolve(
+ Tcl_Interp *interp,
+ Object *slot,
+ Tcl_Obj *item)
+{
+ Tcl_Obj *resolveArgs[] = {
+ slot->fPtr->myName,
+ slot->fPtr->slotResolveName,
+ item
+ };
+ int code = TclOOPrivateObjectCmd(slot, interp, 3, resolveArgs);
+ if (code != TCL_OK) {
+ return NULL;
+ }
+ return Tcl_GetObjResult(interp);
+}
+
+static inline Tcl_Obj *
+ResolveAll(
+ Tcl_Interp *interp,
+ Object *slot,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Tcl_Obj **resolvedItems = (Tcl_Obj **) TclStackAlloc(interp,
+ sizeof(Tcl_Obj *) * objc);
+ for (int i = 0; i < objc; i++) {
+ resolvedItems[i] = CallSlotResolve(interp, slot, objv[i]);
+ if (resolvedItems[i] == NULL) {
+ for (int j = 0; j < i; j++) {
+ Tcl_DecrRefCount(resolvedItems[j]);
+ }
+ TclStackFree(interp, (void *) resolvedItems);
+ return NULL;
+ }
+ Tcl_IncrRefCount(resolvedItems[i]);
+ Tcl_ResetResult(interp);
+ }
+ Tcl_Obj *resolvedList = Tcl_NewListObj(objc, resolvedItems);
+ for (int i = 0; i < objc; i++) {
+ TclDecrRefCount(resolvedItems[i]);
+ }
+ TclStackFree(interp, (void *) resolvedItems);
+ return resolvedList;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Append --
+ *
+ * Implementation of the "-append" slot operation.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Append(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip == objc) {
+ return TCL_OK;
+ }
+
+ // Resolve all values
+ Tcl_Obj *resolved = ResolveAll(interp, oPtr, objc - skip, objv + skip);
+ if (resolved == NULL) {
+ return TCL_ERROR;
+ }
+
+ // Get slot contents; store in list
+ Tcl_Obj *list = CallSlotGet(interp, oPtr);
+ if (list == NULL) {
+ Tcl_DecrRefCount(resolved);
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(list);
+ Tcl_ResetResult(interp);
+
+ // Append
+ if (Tcl_IsShared(list)) {
+ Tcl_Obj *dup = Tcl_DuplicateObj(list);
+ Tcl_IncrRefCount(dup);
+ Tcl_DecrRefCount(list);
+ list = dup;
+ }
+ if (Tcl_ListObjAppendList(interp, list, resolved) != TCL_OK) {
+ Tcl_DecrRefCount(list);
+ Tcl_DecrRefCount(resolved);
+ return TCL_ERROR;
+ }
+ Tcl_DecrRefCount(resolved);
+
+ // Set slot contents
+ int code = CallSlotSet(interp, oPtr, list);
+ Tcl_DecrRefCount(list);
+ return code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_AppendNew --
+ *
+ * Implementation of the "-appendifnew" slot operation.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_AppendNew(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip == objc) {
+ return TCL_OK;
+ }
+
+ // Resolve all values
+ Tcl_Obj *resolved = ResolveAll(interp, oPtr, objc - skip, objv + skip);
+ if (resolved == NULL) {
+ return TCL_ERROR;
+ }
+
+ // Get slot contents; store in list
+ Tcl_Obj *list = CallSlotGet(interp, oPtr);
+ if (list == NULL) {
+ Tcl_DecrRefCount(resolved);
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(list);
+ Tcl_ResetResult(interp);
+
+ // Prepare a set of items in the list to set
+ Tcl_Size listc;
+ Tcl_Obj **listv;
+ if (TclListObjGetElements(interp, list, &listc, &listv) != TCL_OK) {
+ Tcl_DecrRefCount(list);
+ Tcl_DecrRefCount(resolved);
+ return TCL_ERROR;
+ }
+ Tcl_HashTable unique;
+ Tcl_InitObjHashTable(&unique);
+ for (Tcl_Size i=0 ; i<listc; i++) {
+ Tcl_CreateHashEntry(&unique, listv[i], NULL);
+ }
+
+ // Append the new items if they're not already there
+ if (Tcl_IsShared(list)) {
+ Tcl_Obj *dup = Tcl_DuplicateObj(list);
+ Tcl_IncrRefCount(dup);
+ Tcl_DecrRefCount(list);
+ list = dup;
+ }
+ TclListObjGetElements(NULL, resolved, &listc, &listv);
+ for (Tcl_Size i=0 ; i<listc; i++) {
+ int isNew;
+ Tcl_CreateHashEntry(&unique, listv[i], &isNew);
+ if (isNew) {
+ Tcl_ListObjAppendElement(interp, list, listv[i]);
+ }
+ }
+ Tcl_DecrRefCount(resolved);
+ Tcl_DeleteHashTable(&unique);
+
+ // Set slot contents
+ int code = CallSlotSet(interp, oPtr, list);
+ Tcl_DecrRefCount(list);
+ return code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Clear --
+ *
+ * Implementation of the "-clear" slot operation.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Clear(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip != objc) {
+ Tcl_WrongNumArgs(interp, skip, objv, NULL);
+ return TCL_ERROR;
+ }
+ Tcl_Obj *list = Tcl_NewObj();
+ Tcl_IncrRefCount(list);
+ int code = CallSlotSet(interp, oPtr, list);
+ Tcl_DecrRefCount(list);
+ return code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Prepend --
+ *
+ * Implementation of the "-prepend" slot operation.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Prepend(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip == objc) {
+ return TCL_OK;
+ }
+
+ // Resolve all values
+ Tcl_Obj *list = ResolveAll(interp, oPtr, objc - skip, objv + skip);
+ if (list == NULL) {
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(list);
+
+ // Get slot contents and append to list
+ Tcl_Obj *oldList = CallSlotGet(interp, oPtr);
+ if (oldList == NULL) {
+ Tcl_DecrRefCount(list);
+ return TCL_ERROR;
+ }
+ Tcl_ListObjAppendList(NULL, list, oldList);
+ Tcl_ResetResult(interp);
+
+ // Set slot contents
+ int code = CallSlotSet(interp, oPtr, list);
+ Tcl_DecrRefCount(list);
+ return code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Remove --
+ *
+ * Implementation of the "-remove" slot operation.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Remove(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip == objc) {
+ return TCL_OK;
+ }
+
+ // Resolve all values
+ Tcl_Obj *resolved = ResolveAll(interp, oPtr, objc - skip, objv + skip);
+ if (resolved == NULL) {
+ return TCL_ERROR;
+ }
+
+ // Get slot contents; store in list
+ Tcl_Obj *oldList = CallSlotGet(interp, oPtr);
+ if (oldList == NULL) {
+ Tcl_DecrRefCount(resolved);
+ return TCL_ERROR;
+ }
+ Tcl_IncrRefCount(oldList);
+ Tcl_ResetResult(interp);
+
+ // Prepare a set of items in the list to remove
+ Tcl_Size listc;
+ Tcl_Obj **listv;
+ TclListObjGetElements(NULL, resolved, &listc, &listv);
+ Tcl_HashTable removeSet;
+ Tcl_InitObjHashTable(&removeSet);
+ for (Tcl_Size i=0 ; i<listc; i++) {
+ Tcl_CreateHashEntry(&removeSet, listv[i], NULL);
+ }
+ Tcl_DecrRefCount(resolved);
+
+ // Append the new items from the old items if they're not in the remove set
+ if (TclListObjGetElements(interp, oldList, &listc, &listv) != TCL_OK) {
+ Tcl_DecrRefCount(oldList);
+ Tcl_DeleteHashTable(&removeSet);
+ return TCL_ERROR;
+ }
+ Tcl_Obj *newList = Tcl_NewObj();
+ for (Tcl_Size i=0 ; i<listc; i++) {
+ if (Tcl_FindHashEntry(&removeSet, listv[i]) == NULL) {
+ Tcl_ListObjAppendElement(NULL, newList, listv[i]);
+ }
+ }
+ Tcl_DecrRefCount(oldList);
+ Tcl_DeleteHashTable(&removeSet);
+
+ // Set slot contents
+ Tcl_IncrRefCount(newList);
+ int code = CallSlotSet(interp, oPtr, newList);
+ Tcl_DecrRefCount(newList);
+ return code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Resolve --
+ *
+ * Default implementation of the "Resolve" slot accessor. Just returns
+ * its argument unchanged; particular slots may override.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Resolve(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip + 1 != objc) {
+ Tcl_WrongNumArgs(interp, skip, objv, "list");
+ return TCL_ERROR;
+ }
+ Tcl_SetObjResult(interp, objv[objc - 1]);
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Set --
+ *
+ * Implementation of the "-set" slot operation.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Set(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ Tcl_Obj *list;
+
+ // Resolve all values
+ if (skip == objc) {
+ list = Tcl_NewObj();
+ } else {
+ list = ResolveAll(interp, oPtr, objc - skip, objv + skip);
+ if (list == NULL) {
+ return TCL_ERROR;
+ }
+ }
+ Tcl_IncrRefCount(list);
+
+ // Set slot contents
+ int code = CallSlotSet(interp, oPtr, list);
+ Tcl_DecrRefCount(list);
+ return code;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Unimplemented --
+ *
+ * Default implementation of the "Get" and "Set" slot accessors. Just
+ * returns an error; actual slots must override.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Unimplemented(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ TCL_UNUSED(Tcl_ObjectContext),
+ TCL_UNUSED(int),
+ TCL_UNUSED(Tcl_Obj *const *))
+{
+ Tcl_SetObjResult(interp, Tcl_NewStringObj("unimplemented", -1));
+ OO_ERROR(interp, ABSTRACT_SLOT);
+ return TCL_ERROR;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Slot_Unknown --
+ *
+ * Unknown method name handler for slots. Delegates to the default slot
+ * operation (--default-operation forwarded method) unless the first
+ * argument starts with a dash.
+ *
+ * ----------------------------------------------------------------------
+ */
+static int
+Slot_Unknown(
+ TCL_UNUSED(void *),
+ Tcl_Interp *interp,
+ Tcl_ObjectContext context,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ Object *oPtr = (Object *) Tcl_ObjectContextObject(context);
+ int skip = Tcl_ObjectContextSkippedArgs(context);
+ if (skip >= objc) {
+ Tcl_Obj *args[] = {
+ oPtr->fPtr->myName,
+ oPtr->fPtr->slotDefOpName
+ };
+ return TclOOPrivateObjectCmd(oPtr, interp, 2, args);
+ } else if (TclGetString(objv[skip])[0] != '-') {
+ Tcl_Obj **args = (Tcl_Obj **) TclStackAlloc(interp,
+ sizeof(Tcl_Obj *) * (objc - skip + 2));
+ args[0] = oPtr->fPtr->myName;
+ args[1] = oPtr->fPtr->slotDefOpName;
+ memcpy(args+2, objv+skip, sizeof(Tcl_Obj*) * (objc - skip));
+ int code = TclOOPrivateObjectCmd(oPtr, interp, objc - skip + 2, args);
+ TclStackFree(interp, args);
+ return code;
+ }
+ return TclNRObjectContextInvokeNext(interp, context, objc, objv, skip);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* ClassFilter_Get, ClassFilter_Set --
*
* Implementation of the "filter" slot accessors of the "oo::define"
diff --git a/generic/tclOOInt.h b/generic/tclOOInt.h
index e4351f6..777c7fa 100644
--- a/generic/tclOOInt.h
+++ b/generic/tclOOInt.h
@@ -255,6 +255,7 @@ struct Object {
PropertyStorage properties; /* Information relating to the lists of
* properties that this object *claims* to
* support. */
+ Tcl_Obj *linkedCmdsList; /* List of names of linked commands. */
};
enum ObjectFlags {
@@ -405,8 +406,10 @@ struct Foundation {
* "<cloned>" pseudo-constructor. */
Tcl_Obj *defineName; /* Fully qualified name of oo::define. */
Tcl_Obj *myName; /* The "my" shared object. */
- Tcl_Obj *mcdName; /* The shared object for calling the helper to
- * mix in class delegates. */
+ Tcl_Obj *slotGetName; /* The "Get" name used by slots. */
+ Tcl_Obj *slotSetName; /* The "Set" name used by slots. */
+ Tcl_Obj *slotResolveName; /* The "Resolve" name used by slots. */
+ Tcl_Obj *slotDefOpName; /* The "--default-operation" name used by slots. */
};
/*
@@ -498,6 +501,7 @@ struct DeclaredClassMethod {
MODULE_SCOPE int TclOOInit(Tcl_Interp *interp);
MODULE_SCOPE Tcl_ObjCmdProc TclOODefineObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOOObjDefObjCmd;
+MODULE_SCOPE Tcl_ObjCmdProc TclOODefineClassMethodObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOODefineConstructorObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOODefineDefnNsObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOODefineDeleteMethodObjCmd;
@@ -515,7 +519,10 @@ MODULE_SCOPE Tcl_ObjCmdProc TclOODefinePrivateObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOODefinePropertyCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOOUnknownDefinition;
MODULE_SCOPE Tcl_ObjCmdProc TclOOCallbackObjCmd;
+MODULE_SCOPE Tcl_ObjCmdProc TclOOClassVariableObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOOCopyObjectCmd;
+MODULE_SCOPE Tcl_ObjCmdProc TclOODelegateNameObjCmd;
+MODULE_SCOPE Tcl_ObjCmdProc TclOOLinkObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOONextObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOONextToObjCmd;
MODULE_SCOPE Tcl_ObjCmdProc TclOOSelfObjCmd;
diff --git a/generic/tclOOScript.h b/generic/tclOOScript.h
index a9b262c..390b034 100644
--- a/generic/tclOOScript.h
+++ b/generic/tclOOScript.h
@@ -27,157 +27,6 @@
static const char *tclOOSetupScript =
/* !BEGIN!: Do not edit below this line. */
"::namespace eval ::oo {\n"
-"\tproc Helpers::classvariable {name args} {\n"
-"\t\tset ns [info object namespace [uplevel 1 {self class}]]\n"
-"\t\tforeach v [list $name {*}$args] {\n"
-"\t\t\tif {[string match *(*) $v]} {\n"
-"\t\t\t\tset reason \"can\'t create a scalar variable that looks like an array element\"\n"
-"\t\t\t\treturn -code error -errorcode {TCL UPVAR LOCAL_ELEMENT} \\\n"
-"\t\t\t\t\t[format {bad variable name \"%s\": %s} $v $reason]\n"
-"\t\t\t}\n"
-"\t\t\tif {[string match *::* $v]} {\n"
-"\t\t\t\tset reason \"can\'t create a local variable with a namespace separator in it\"\n"
-"\t\t\t\treturn -code error -errorcode {TCL UPVAR INVERTED} \\\n"
-"\t\t\t\t\t[format {bad variable name \"%s\": %s} $v $reason]\n"
-"\t\t\t}\n"
-"\t\t\tlappend vs $v $v\n"
-"\t\t}\n"
-"\t\ttailcall namespace upvar $ns {*}$vs\n"
-"\t}\n"
-"\tproc Helpers::link {args} {\n"
-"\t\tset ns [uplevel 1 {::namespace current}]\n"
-"\t\tforeach link $args {\n"
-"\t\t\tif {[llength $link] == 2} {\n"
-"\t\t\t\tlassign $link src dst\n"
-"\t\t\t} elseif {[llength $link] == 1} {\n"
-"\t\t\t\tlassign $link src\n"
-"\t\t\t\tset dst $src\n"
-"\t\t\t} else {\n"
-"\t\t\t\treturn -code error -errorcode {TCL OO CMDLINK_FORMAT} \\\n"
-"\t\t\t\t\t\"bad link description; must only have one or two elements\"\n"
-"\t\t\t}\n"
-"\t\t\tif {![string match ::* $src]} {\n"
-"\t\t\t\tset src [string cat $ns :: $src]\n"
-"\t\t\t}\n"
-"\t\t\tinterp alias {} $src {} ${ns}::my $dst\n"
-"\t\t\ttrace add command ${ns}::my delete [list \\\n"
-"\t\t\t\t::oo::UnlinkLinkedCommand $src]\n"
-"\t\t}\n"
-"\t}\n"
-"\tproc UnlinkLinkedCommand {cmd args} {\n"
-"\t\tif {[namespace which $cmd] ne {}} {\n"
-"\t\t\trename $cmd {}\n"
-"\t\t}\n"
-"\t}\n"
-"\tproc DelegateName {class} {\n"
-"\t\tstring cat [info object namespace $class] {:: oo ::delegate}\n"
-"\t}\n"
-"\tproc MixinClassDelegates {class} {\n"
-"\t\tif {![info object isa class $class]} {\n"
-"\t\t\treturn\n"
-"\t\t}\n"
-"\t\tset delegate [DelegateName $class]\n"
-"\t\tif {![info object isa class $delegate]} {\n"
-"\t\t\treturn\n"
-"\t\t}\n"
-"\t\tforeach c [info class superclass $class] {\n"
-"\t\t\tset d [DelegateName $c]\n"
-"\t\t\tif {![info object isa class $d]} {\n"
-"\t\t\t\tcontinue\n"
-"\t\t\t}\n"
-"\t\t\tdefine $delegate ::oo::define::superclass -appendifnew $d\n"
-"\t\t}\n"
-"\t\tobjdefine $class ::oo::objdefine::mixin -appendifnew $delegate\n"
-"\t}\n"
-"\tproc UpdateClassDelegatesAfterClone {originObject targetObject} {\n"
-"\t\tset originDelegate [DelegateName $originObject]\n"
-"\t\tset targetDelegate [DelegateName $targetObject]\n"
-"\t\tif {\n"
-"\t\t\t[info object isa class $originDelegate]\n"
-"\t\t\t&& ![info object isa class $targetDelegate]\n"
-"\t\t} then {\n"
-"\t\t\tcopy $originDelegate $targetDelegate\n"
-"\t\t\tobjdefine $targetObject ::oo::objdefine::mixin -set \\\n"
-"\t\t\t\t{*}[lmap c [info object mixin $targetObject] {\n"
-"\t\t\t\t\tif {$c eq $originDelegate} {set targetDelegate} {set c}\n"
-"\t\t\t\t}]\n"
-"\t\t}\n"
-"\t}\n"
-"\tproc define::classmethod {name args} {\n"
-"\t\t::set argc [::llength [::info level 0]]\n"
-"\t\t::if {$argc == 3} {\n"
-"\t\t\t::return -code error -errorcode {TCL WRONGARGS} [::format \\\n"
-"\t\t\t\t{wrong # args: should be \"%s name \?args body\?\"} \\\n"
-"\t\t\t\t[::lindex [::info level 0] 0]]\n"
-"\t\t}\n"
-"\t\t::set cls [::uplevel 1 self]\n"
-"\t\t::if {$argc == 4} {\n"
-"\t\t\t::oo::define [::oo::DelegateName $cls] method $name {*}$args\n"
-"\t\t}\n"
-"\t\t::tailcall forward $name myclass $name\n"
-"\t}\n"
-"\tdefine Slot {\n"
-"\t\tmethod Get -unexport {} {\n"
-"\t\t\treturn -code error -errorcode {TCL OO ABSTRACT_SLOT} \"unimplemented\"\n"
-"\t\t}\n"
-"\t\tmethod Set -unexport list {\n"
-"\t\t\treturn -code error -errorcode {TCL OO ABSTRACT_SLOT} \"unimplemented\"\n"
-"\t\t}\n"
-"\t\tmethod Resolve -unexport list {\n"
-"\t\t\treturn $list\n"
-"\t\t}\n"
-"\t\tmethod -set -export args {\n"
-"\t\t\tset my [namespace which my]\n"
-"\t\t\tset args [lmap a $args {uplevel 1 [list $my Resolve $a]}]\n"
-"\t\t\ttailcall my Set $args\n"
-"\t\t}\n"
-"\t\tmethod -append -export args {\n"
-"\t\t\tset my [namespace which my]\n"
-"\t\t\tset args [lmap a $args {uplevel 1 [list $my Resolve $a]}]\n"
-"\t\t\tset current [uplevel 1 [list $my Get]]\n"
-"\t\t\ttailcall my Set [list {*}$current {*}$args]\n"
-"\t\t}\n"
-"\t\tmethod -appendifnew -export args {\n"
-"\t\t\tset my [namespace which my]\n"
-"\t\t\tset current [uplevel 1 [list $my Get]]\n"
-"\t\t\tforeach a $args {\n"
-"\t\t\t\tset a [uplevel 1 [list $my Resolve $a]]\n"
-"\t\t\t\tif {$a ni $current} {\n"
-"\t\t\t\t\tlappend current $a\n"
-"\t\t\t\t}\n"
-"\t\t\t}\n"
-"\t\t\ttailcall my Set $current\n"
-"\t\t}\n"
-"\t\tmethod -clear -export {} {tailcall my Set {}}\n"
-"\t\tmethod -prepend -export args {\n"
-"\t\t\tset my [namespace which my]\n"
-"\t\t\tset args [lmap a $args {uplevel 1 [list $my Resolve $a]}]\n"
-"\t\t\tset current [uplevel 1 [list $my Get]]\n"
-"\t\t\ttailcall my Set [list {*}$args {*}$current]\n"
-"\t\t}\n"
-"\t\tmethod -remove -export args {\n"
-"\t\t\tset my [namespace which my]\n"
-"\t\t\tset args [lmap a $args {uplevel 1 [list $my Resolve $a]}]\n"
-"\t\t\tset current [uplevel 1 [list $my Get]]\n"
-"\t\t\ttailcall my Set [lmap val $current {\n"
-"\t\t\t\tif {$val in $args} continue else {set val}\n"
-"\t\t\t}]\n"
-"\t\t}\n"
-"\t\tforward --default-operation my -append\n"
-"\t\tmethod unknown -unexport {args} {\n"
-"\t\t\tset def --default-operation\n"
-"\t\t\tif {[llength $args] == 0} {\n"
-"\t\t\t\ttailcall my $def\n"
-"\t\t\t} elseif {![string match -* [lindex $args 0]]} {\n"
-"\t\t\t\ttailcall my $def {*}$args\n"
-"\t\t\t}\n"
-"\t\t\tnext {*}$args\n"
-"\t\t}\n"
-"\t\tunexport destroy\n"
-"\t}\n"
-"\tobjdefine define::superclass forward --default-operation my -set\n"
-"\tobjdefine define::mixin forward --default-operation my -set\n"
-"\tobjdefine objdefine::mixin forward --default-operation my -set\n"
"\tdefine object method <cloned> -unexport {originObject} {\n"
"\t\tforeach p [info procs [info object namespace $originObject]::*] {\n"
"\t\t\tset args [info args $p]\n"
@@ -206,34 +55,44 @@ static const char *tclOOSetupScript =
"\t\t}\n"
"\t}\n"
"\tdefine class method <cloned> -unexport {originObject} {\n"
+"\t\tset targetObject [self]\n"
"\t\tnext $originObject\n"
-"\t\t::oo::UpdateClassDelegatesAfterClone $originObject [self]\n"
+"\t\tset originDelegate [::oo::DelegateName $originObject]\n"
+"\t\tset targetDelegate [::oo::DelegateName $targetObject]\n"
+"\t\tif {\n"
+"\t\t\t[info object isa class $originDelegate]\n"
+"\t\t\t&& ![info object isa class $targetDelegate]\n"
+"\t\t} then {\n"
+"\t\t\t::oo::copy $originDelegate $targetDelegate\n"
+"\t\t\t::oo::objdefine $targetObject mixin -set \\\n"
+"\t\t\t\t{*}[lmap c [info object mixin $targetObject] {\n"
+"\t\t\t\t\tif {$c eq $originDelegate} {set targetDelegate} {set c}\n"
+"\t\t\t\t}]\n"
+"\t\t}\n"
"\t}\n"
-"\tclass create singleton {\n"
-"\t\tsuperclass -set class\n"
-"\t\tvariable -set object\n"
-"\t\tunexport create createWithNamespace\n"
-"\t\tmethod new args {\n"
-"\t\t\tif {![info exists object] || ![info object isa object $object]} {\n"
-"\t\t\t\tset object [next {*}$args]\n"
-"\t\t\t\t::oo::objdefine $object {\n"
-"\t\t\t\t\tmethod destroy {} {\n"
-"\t\t\t\t\t\t::return -code error -errorcode {TCL OO SINGLETON} \\\n"
-"\t\t\t\t\t\t\t\"may not destroy a singleton object\"\n"
-"\t\t\t\t\t}\n"
-"\t\t\t\t\tmethod <cloned> -unexport {originObject} {\n"
-"\t\t\t\t\t\t::return -code error -errorcode {TCL OO SINGLETON} \\\n"
-"\t\t\t\t\t\t\t\"may not clone a singleton object\"\n"
-"\t\t\t\t\t}\n"
-"\t\t\t\t}\n"
-"\t\t\t}\n"
-"\t\t\treturn $object\n"
+"\tclass create singleton\n"
+"\tdefine singleton superclass -set class\n"
+"\tdefine singleton unexport create createWithNamespace\n"
+"\tdefine singleton method new args {\n"
+"\t\tvariable object\n"
+"\t\tif {![info exists object] || ![info object isa object $object]} {\n"
+"\t\t\tset object [next {*}$args]\n"
+"\t\t\t::oo::objdefine $object mixin -prepend ::oo::SingletonInstance\n"
"\t\t}\n"
+"\t\treturn $object\n"
"\t}\n"
-"\tclass create abstract {\n"
-"\t\tsuperclass -set class\n"
-"\t\tunexport create createWithNamespace new\n"
+"\tclass create SingletonInstance\n"
+"\tdefine SingletonInstance method destroy {} {\n"
+"\t\treturn -code error -errorcode {TCL OO SINGLETON} \\\n"
+"\t\t\t\"may not destroy a singleton object\"\n"
"\t}\n"
+"\tdefine SingletonInstance method <cloned> -unexport {originObject} {\n"
+"\t\treturn -code error -errorcode {TCL OO SINGLETON} \\\n"
+"\t\t\t\"may not clone a singleton object\"\n"
+"\t}\n"
+"\tclass create abstract\n"
+"\tdefine abstract superclass -set class\n"
+"\tdefine abstract unexport create createWithNamespace new\n"
"\tnamespace eval configuresupport::configurableclass {\n"
"\t\t::proc properties args {::tailcall property {*}$args}\n"
"\t\t::namespace path ::oo::define\n"
@@ -248,14 +107,13 @@ static const char *tclOOSetupScript =
"\t\tdefinitionnamespace -instance configuresupport::configurableobject\n"
"\t\tdefinitionnamespace -class configuresupport::configurableclass\n"
"\t}\n"
-"\tclass create configurable {\n"
-"\t\tsuperclass -set class\n"
-"\t\tconstructor {{definitionScript \"\"}} {\n"
-"\t\t\tnext {mixin ::oo::configuresupport::configurable}\n"
-"\t\t\tnext $definitionScript\n"
-"\t\t}\n"
-"\t\tdefinitionnamespace -class configuresupport::configurableclass\n"
+"\tclass create configurable\n"
+"\tdefine configurable superclass -set class\n"
+"\tdefine configurable constructor {{definitionScript \"\"}} {\n"
+"\t\t::oo::define [self] {mixin -append ::oo::configuresupport::configurable}\n"
+"\t\tnext $definitionScript\n"
"\t}\n"
+"\tdefine configurable definitionnamespace -class configuresupport::configurableclass\n"
"}\n"
/* !END!: Do not edit above this line. */
;
diff --git a/generic/tclStringObj.c b/generic/tclStringObj.c
index 13fbdbc..c33860d 100644
--- a/generic/tclStringObj.c
+++ b/generic/tclStringObj.c
@@ -235,6 +235,10 @@ Tcl_NewStringObj(
{
return Tcl_DbNewStringObj(bytes, length, "unknown", 0);
}
+
+// Redefine the macro
+#define Tcl_NewStringObj(bytes, len) \
+ Tcl_DbNewStringObj(bytes, len, __FILE__, __LINE__)
#else /* if not TCL_MEM_DEBUG */
Tcl_Obj *
Tcl_NewStringObj(
diff --git a/tests/oo.test b/tests/oo.test
index 21c8f9e..7d5ea37 100644
--- a/tests/oo.test
+++ b/tests/oo.test
@@ -390,7 +390,7 @@ test oo-1.21 {basic test of OO functionality: default relations} -setup {
}] {lsort [lsearch -all -not -inline $x *::delegate]}
} -cleanup {
interp delete $fresh
-} -result {{} {::oo::Slot ::oo::abstract ::oo::class ::oo::configurable ::oo::configuresupport::configurable ::oo::object ::oo::singleton} {::oo::configuresupport::objreadableproperties ::oo::configuresupport::objwritableproperties ::oo::configuresupport::readableproperties ::oo::configuresupport::writableproperties ::oo::define::filter ::oo::define::mixin ::oo::define::superclass ::oo::define::variable ::oo::objdefine::filter ::oo::objdefine::mixin ::oo::objdefine::variable} {::oo::Slot ::oo::class ::oo::configuresupport::configurable} {::oo::abstract ::oo::configurable ::oo::singleton} {} {} {} {} {} ::oo::object ::oo::object ::oo::class ::oo::class ::oo::class}
+} -result {{} {::oo::SingletonInstance ::oo::Slot ::oo::abstract ::oo::class ::oo::configurable ::oo::configuresupport::configurable ::oo::object ::oo::singleton} {::oo::configuresupport::objreadableproperties ::oo::configuresupport::objwritableproperties ::oo::configuresupport::readableproperties ::oo::configuresupport::writableproperties ::oo::define::filter ::oo::define::mixin ::oo::define::superclass ::oo::define::variable ::oo::objdefine::filter ::oo::objdefine::mixin ::oo::objdefine::variable} {::oo::SingletonInstance ::oo::Slot ::oo::class ::oo::configuresupport::configurable} {::oo::abstract ::oo::configurable ::oo::singleton} {} {} {} {} {} ::oo::object ::oo::object ::oo::class ::oo::class ::oo::class}
test oo-1.22 {basic test of OO functionality: nested ownership destruction order} -setup {
oo::class create parent
} -body {
diff --git a/tools/tclOOScript.tcl b/tools/tclOOScript.tcl
index 542b711..b17d7d0 100644
--- a/tools/tclOOScript.tcl
+++ b/tools/tclOOScript.tcl
@@ -12,299 +12,6 @@
# this file, and for a DISCLAIMER OF ALL WARRANTIES.
::namespace eval ::oo {
-
- #
- # Commands that are made available to objects by default.
- #
-
- # ------------------------------------------------------------------
- #
- # classvariable --
- #
- # Link to a variable in the class of the current object.
- #
- # ------------------------------------------------------------------
-
- proc Helpers::classvariable {name args} {
- # Get a reference to the class's namespace
- set ns [info object namespace [uplevel 1 {self class}]]
- # Double up the list of variable names
- foreach v [list $name {*}$args] {
- if {[string match *(*) $v]} {
- set reason "can't create a scalar variable that looks like an array element"
- return -code error -errorcode {TCL UPVAR LOCAL_ELEMENT} \
- [format {bad variable name "%s": %s} $v $reason]
- }
- if {[string match *::* $v]} {
- set reason "can't create a local variable with a namespace separator in it"
- return -code error -errorcode {TCL UPVAR INVERTED} \
- [format {bad variable name "%s": %s} $v $reason]
- }
- lappend vs $v $v
- }
- # Lastly, link the caller's local variables to the class's variables
- tailcall namespace upvar $ns {*}$vs
- }
-
- # ------------------------------------------------------------------
- #
- # link --
- #
- # Make a command that invokes a method on the current object.
- # The name of the command and the name of the method match by
- # default.
- #
- # ------------------------------------------------------------------
-
- proc Helpers::link {args} {
- set ns [uplevel 1 {::namespace current}]
- foreach link $args {
- if {[llength $link] == 2} {
- lassign $link src dst
- } elseif {[llength $link] == 1} {
- lassign $link src
- set dst $src
- } else {
- return -code error -errorcode {TCL OO CMDLINK_FORMAT} \
- "bad link description; must only have one or two elements"
- }
- if {![string match ::* $src]} {
- set src [string cat $ns :: $src]
- }
- interp alias {} $src {} ${ns}::my $dst
- trace add command ${ns}::my delete [list \
- ::oo::UnlinkLinkedCommand $src]
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # UnlinkLinkedCommand --
- #
- # Callback used to remove linked command when the underlying mechanism
- # that supports it is deleted.
- #
- # ----------------------------------------------------------------------
-
- proc UnlinkLinkedCommand {cmd args} {
- if {[namespace which $cmd] ne {}} {
- rename $cmd {}
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # DelegateName --
- #
- # Utility that gets the name of the class delegate for a class. It's
- # trivial, but makes working with them much easier as delegate names are
- # intentionally hard to create by accident.
- #
- # ----------------------------------------------------------------------
-
- proc DelegateName {class} {
- string cat [info object namespace $class] {:: oo ::delegate}
- }
-
- # ----------------------------------------------------------------------
- #
- # MixinClassDelegates --
- #
- # Support code called *after* [oo::define] inside the constructor of a
- # class that patches in the appropriate class delegates.
- #
- # ----------------------------------------------------------------------
-
- proc MixinClassDelegates {class} {
- if {![info object isa class $class]} {
- return
- }
- set delegate [DelegateName $class]
- if {![info object isa class $delegate]} {
- return
- }
- foreach c [info class superclass $class] {
- set d [DelegateName $c]
- if {![info object isa class $d]} {
- continue
- }
- define $delegate ::oo::define::superclass -appendifnew $d
- }
- objdefine $class ::oo::objdefine::mixin -appendifnew $delegate
- }
-
- # ----------------------------------------------------------------------
- #
- # UpdateClassDelegatesAfterClone --
- #
- # Support code that is like [MixinClassDelegates] except for when a
- # class is cloned.
- #
- # ----------------------------------------------------------------------
-
- proc UpdateClassDelegatesAfterClone {originObject targetObject} {
- # Rebuild the class inheritance delegation class
- set originDelegate [DelegateName $originObject]
- set targetDelegate [DelegateName $targetObject]
- if {
- [info object isa class $originDelegate]
- && ![info object isa class $targetDelegate]
- } then {
- copy $originDelegate $targetDelegate
- objdefine $targetObject ::oo::objdefine::mixin -set \
- {*}[lmap c [info object mixin $targetObject] {
- if {$c eq $originDelegate} {set targetDelegate} {set c}
- }]
- }
- }
-
- # ----------------------------------------------------------------------
- #
- # oo::define::classmethod --
- #
- # Defines a class method. See define(n) for details.
- #
- # Note that the ::oo::define namespace is semi-public and a bit weird
- # anyway, so we don't regard the namespace path as being under control:
- # fully qualified names are used for everything.
- #
- # ----------------------------------------------------------------------
-
- proc define::classmethod {name args} {
- # Create the method on the class if the caller gave arguments and body
- ::set argc [::llength [::info level 0]]
- ::if {$argc == 3} {
- ::return -code error -errorcode {TCL WRONGARGS} [::format \
- {wrong # args: should be "%s name ?args body?"} \
- [::lindex [::info level 0] 0]]
- }
- ::set cls [::uplevel 1 self]
- ::if {$argc == 4} {
- ::oo::define [::oo::DelegateName $cls] method $name {*}$args
- }
- # Make the connection by forwarding
- ::tailcall forward $name myclass $name
- }
-
- # ----------------------------------------------------------------------
- #
- # Slot --
- #
- # The class of slot operations, which are basically lists at the low
- # level of TclOO; this provides a more consistent interface to them.
- #
- # ----------------------------------------------------------------------
-
- define Slot {
- # ------------------------------------------------------------------
- #
- # Slot Get --
- #
- # Basic slot getter. Retrieves the contents of the slot.
- # Particular slots must provide concrete non-erroring
- # implementation.
- #
- # ------------------------------------------------------------------
-
- method Get -unexport {} {
- return -code error -errorcode {TCL OO ABSTRACT_SLOT} "unimplemented"
- }
-
- # ------------------------------------------------------------------
- #
- # Slot Set --
- #
- # Basic slot setter. Sets the contents of the slot. Particular
- # slots must provide concrete non-erroring implementation.
- #
- # ------------------------------------------------------------------
-
- method Set -unexport list {
- return -code error -errorcode {TCL OO ABSTRACT_SLOT} "unimplemented"
- }
-
- # ------------------------------------------------------------------
- #
- # Slot Resolve --
- #
- # Helper that lets a slot convert a list of arguments of a
- # particular type to their canonical forms. Defaults to doing
- # nothing (suitable for simple strings).
- #
- # ------------------------------------------------------------------
-
- method Resolve -unexport list {
- return $list
- }
-
- # ------------------------------------------------------------------
- #
- # Slot -set, -append, -clear, --default-operation --
- #
- # Standard public slot operations. If a slot can't figure out
- # what method to call directly, it uses --default-operation.
- #
- # ------------------------------------------------------------------
-
- method -set -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- tailcall my Set $args
- }
- method -append -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- set current [uplevel 1 [list $my Get]]
- tailcall my Set [list {*}$current {*}$args]
- }
- method -appendifnew -export args {
- set my [namespace which my]
- set current [uplevel 1 [list $my Get]]
- foreach a $args {
- set a [uplevel 1 [list $my Resolve $a]]
- if {$a ni $current} {
- lappend current $a
- }
- }
- tailcall my Set $current
- }
- method -clear -export {} {tailcall my Set {}}
- method -prepend -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- set current [uplevel 1 [list $my Get]]
- tailcall my Set [list {*}$args {*}$current]
- }
- method -remove -export args {
- set my [namespace which my]
- set args [lmap a $args {uplevel 1 [list $my Resolve $a]}]
- set current [uplevel 1 [list $my Get]]
- tailcall my Set [lmap val $current {
- if {$val in $args} continue else {set val}
- }]
- }
-
- # Default handling
- forward --default-operation my -append
- method unknown -unexport {args} {
- set def --default-operation
- if {[llength $args] == 0} {
- tailcall my $def
- } elseif {![string match -* [lindex $args 0]]} {
- tailcall my $def {*}$args
- }
- next {*}$args
- }
-
- # Hide destroy
- unexport destroy
- }
-
- # Set the default operation differently for these slots
- objdefine define::superclass forward --default-operation my -set
- objdefine define::mixin forward --default-operation my -set
- objdefine objdefine::mixin forward --default-operation my -set
-
# ----------------------------------------------------------------------
#
# oo::object <cloned> --
@@ -357,9 +64,21 @@
# ----------------------------------------------------------------------
define class method <cloned> -unexport {originObject} {
+ set targetObject [self]
next $originObject
# Rebuild the class inheritance delegation class
- ::oo::UpdateClassDelegatesAfterClone $originObject [self]
+ set originDelegate [::oo::DelegateName $originObject]
+ set targetDelegate [::oo::DelegateName $targetObject]
+ if {
+ [info object isa class $originDelegate]
+ && ![info object isa class $targetDelegate]
+ } then {
+ ::oo::copy $originDelegate $targetDelegate
+ ::oo::objdefine $targetObject mixin -set \
+ {*}[lmap c [info object mixin $targetObject] {
+ if {$c eq $originDelegate} {set targetDelegate} {set c}
+ }]
+ }
}
# ----------------------------------------------------------------------
@@ -371,26 +90,35 @@
#
# ----------------------------------------------------------------------
- class create singleton {
- superclass -set class
- variable -set object
- unexport create createWithNamespace
- method new args {
- if {![info exists object] || ![info object isa object $object]} {
- set object [next {*}$args]
- ::oo::objdefine $object {
- method destroy {} {
- ::return -code error -errorcode {TCL OO SINGLETON} \
- "may not destroy a singleton object"
- }
- method <cloned> -unexport {originObject} {
- ::return -code error -errorcode {TCL OO SINGLETON} \
- "may not clone a singleton object"
- }
- }
- }
- return $object
+ class create singleton
+ define singleton superclass -set class
+ define singleton unexport create createWithNamespace
+ define singleton method new args {
+ variable object
+ if {![info exists object] || ![info object isa object $object]} {
+ set object [next {*}$args]
+ ::oo::objdefine $object mixin -prepend ::oo::SingletonInstance
}
+ return $object
+ }
+
+ # ----------------------------------------------------------------------
+ #
+ # oo::SingletonInstance --
+ #
+ # A mixin used to make an object so it won't be destroyed or cloned (or
+ # at least not easily).
+ #
+ # ----------------------------------------------------------------------
+
+ class create SingletonInstance
+ define SingletonInstance method destroy {} {
+ return -code error -errorcode {TCL OO SINGLETON} \
+ "may not destroy a singleton object"
+ }
+ define SingletonInstance method <cloned> -unexport {originObject} {
+ return -code error -errorcode {TCL OO SINGLETON} \
+ "may not clone a singleton object"
}
# ----------------------------------------------------------------------
@@ -402,10 +130,9 @@
#
# ----------------------------------------------------------------------
- class create abstract {
- superclass -set class
- unexport create createWithNamespace new
- }
+ class create abstract
+ define abstract superclass -set class
+ define abstract unexport create createWithNamespace new
# ----------------------------------------------------------------------
#
@@ -485,16 +212,14 @@
#
# ----------------------------------------------------------------------
- class create configurable {
- superclass -set class
-
- constructor {{definitionScript ""}} {
- next {mixin ::oo::configuresupport::configurable}
- next $definitionScript
- }
-
- definitionnamespace -class configuresupport::configurableclass
+ class create configurable
+ define configurable superclass -set class
+ define configurable constructor {{definitionScript ""}} {
+ ::oo::define [self] {mixin -append ::oo::configuresupport::configurable}
+ next $definitionScript
}
+
+ define configurable definitionnamespace -class configuresupport::configurableclass
}
# Local Variables:
diff --git a/win/Makefile.in b/win/Makefile.in
index 5457bcb..d0e264a 100644
--- a/win/Makefile.in
+++ b/win/Makefile.in
@@ -758,7 +758,7 @@ tclWinPanic.${OBJEXT}: tclWinPanic.c
.rc.$(RES):
$(RC) @RC_OUT@ $@ @RC_TYPE@ @RC_DEFINES@ @RC_INCLUDE@ "$(GENERIC_DIR_NATIVE)" @RC_INCLUDE@ "$(WIN_DIR_NATIVE)" @DEPARG@
-
+tclOO.${OBJEXT}: tclOO.c tclOOScript.h
#--------------------------------------------------------------------------
# Minizip implementation