Index: doc/define.n ================================================================== --- doc/define.n +++ doc/define.n @@ -32,11 +32,15 @@ configuration). The way in which the configuration is done is controlled by either the \fIdefScript\fR argument or by the \fIsubcommand\fR and following \fIarg\fR arguments; when the second is present, it is exactly as if all the arguments from \fIsubcommand\fR onwards are made into a list and that list is used as the \fIdefScript\fR argument. -.SS "CONFIGURING CLASSES" +.PP +Note that the constructor for \fBoo::class\fR will call \fBoo::define\fR on +the script argument that it is provided. This is a convenient way to create +and define a class in one step. +.SH "CONFIGURING CLASSES" .PP The following commands are supported in the \fIdefScript\fR for \fBoo::define\fR, each of which may also be used in the \fIsubcommand\fR form: .TP \fBclassmethod\fI name\fR ?\fIargList bodyScrip\fR? @@ -68,17 +72,15 @@ \fIbodyScript\fR. When the body of the constructor is evaluated, the current namespace of the constructor will be a namespace that is unique to the object being constructed. Within the constructor, the \fBnext\fR command should be used to call the superclasses' constructors. If \fIbodyScript\fR is the empty string, the constructor will be deleted. -.TP -\fBdeletemethod\fI name\fR ?\fIname ...\fR? -. -This deletes each of the methods called \fIname\fR from a class. The methods -must have previously existed in that class. Does not affect the superclasses -of the class, nor does it affect the subclasses or instances of the class -(except when they have a call chain through the class being modified). +.RS +.PP +Classes do not need to have a constructor defined. If none is specified, the +superclass's constructor will be used instead. +.RE .TP \fBdestructor\fI bodyScript\fR . This creates or updates the destructor for a class. Destructors take no arguments, and the body of the destructor will be \fIbodyScript\fR. The @@ -100,20 +102,10 @@ (i.e. usable outside an instance through the instance object's command) by the class being defined. Note that the methods themselves may be actually defined by a superclass; subclass exports override superclass visibility, and may in turn be overridden by instances. .TP -\fBfilter\fR ?\fI\-slotOperation\fR? ?\fImethodName ...\fR? -. -This slot (see \fBSLOTTED DEFINITIONS\fR below) -sets or updates the list of method names that are used to guard whether -method call to instances of the class may be called and what the method's -results are. Each \fImethodName\fR names a single filtering method (which may -be exposed or not exposed); it is not an error for a non-existent method to be -named since they may be defined by subclasses. -By default, this slot works by appending. -.TP \fBforward\fI name cmdName \fR?\fIarg ...\fR? . This creates or updates a forwarded method called \fIname\fR. The method is defined be forwarded to the command called \fIcmdName\fR, with additional arguments, \fIarg\fR etc., added before those arguments specified by the @@ -157,18 +149,10 @@ If in a private definition context (see the \fBprivate\fR definition command, below), this command creates private procedure-like methods. .VE TIP500 .RE .TP -\fBmixin\fR ?\fI\-slotOperation\fR? ?\fIclassName ...\fR? -. -This slot (see \fBSLOTTED DEFINITIONS\fR below) -sets or updates the list of additional classes that are to be mixed into -all the instances of the class being defined. Each \fIclassName\fR argument -names a single class that is to be mixed in. -By default, this slot works by replacement. -.TP \fBprivate \fIcmd arg...\fR .TP \fBprivate \fIscript\fR . .VS TIP500 @@ -184,20 +168,10 @@ commands have no difference in behavior when used in a private definition context. .RE .VE TIP500 .TP -\fBrenamemethod\fI fromName toName\fR -. -This renames the method called \fIfromName\fR in a class to \fItoName\fR. The -method must have previously existed in the class, and \fItoName\fR must not -previously refer to a method in that class. Does not affect the superclasses -of the class, nor does it affect the subclasses or instances of the class -(except when they have a call chain through the class being modified). Does -not change the export status of the method; if it was exported before, it will -be afterwards. -.TP \fBself\fI subcommand arg ...\fR .TP \fBself\fI script\fR .TP \fBself\fR @@ -267,46 +241,88 @@ the methods of this class, and the name of the variable in the instance namespace has a unique prefix that makes accidental use from other classes extremely unlikely. .VE TIP500 .RE -.SS "CONFIGURING OBJECTS" +.SS "ADVANCED CLASS CONFIGURATION OPTIONS" +.PP +The following definitions are also supported, but are not required in simple +programs: +.TP +\fBdefinitionnamespace\fR ?\fIkind\fR? \fInamespaceName\fR +.VS TIP524 +This allows control over what namespace will be used by the \fBoo::define\fR +and \fBoo::objdefine\fR commands to look up the definition commands they +use. When any object has a definition operation applied to it, \fIthe class that +it is an instance of\fR (and its superclasses and mixins) is consulted for +what definition namespace to use. \fBoo::define\fR gets the class definition +namespace, and \fB::oo::objdefine\fR gets the instance definition namespace, +but both otherwise use the identical lookup operation. +.RS +.PP +This sets the definition namespace of kind \fIkind\fR provided by the current +class to \fInamespaceName\fR. The \fInamespaceName\fR must refer to a +currently existing namespace, or must be the empty string (to stop the current +class from having such a namespace connected). The \fIkind\fR, if supplied, +must be either \fB\-class\fR (the default) or \fB\-instance\fR to specify the +whether the namespace for use with \fBoo::define\fR or \fBoo::objdefine\fR +respectively is being set. +.PP +The class \fBoo::object\fR has its instance namespace locked to +\fB::oo::objdefine\fR, and the class \fBoo::class\fR has its class namespace +locked to \fB::oo::define\fR. A consequence of this is that effective use of +this feature for classes requires the definition of a metaclass. +.RE +.VE TIP524 +.TP +\fBdeletemethod\fI name\fR ?\fIname ...\fR? +. +This deletes each of the methods called \fIname\fR from a class. The methods +must have previously existed in that class. Does not affect the superclasses +of the class, nor does it affect the subclasses or instances of the class +(except when they have a call chain through the class being modified). +.TP +\fBfilter\fR ?\fI\-slotOperation\fR? ?\fImethodName ...\fR? +. +This slot (see \fBSLOTTED DEFINITIONS\fR below) +sets or updates the list of method names that are used to guard whether +method call to instances of the class may be called and what the method's +results are. Each \fImethodName\fR names a single filtering method (which may +be exposed or not exposed); it is not an error for a non-existent method to be +named since they may be defined by subclasses. +By default, this slot works by appending. +.TP +\fBmixin\fR ?\fI\-slotOperation\fR? ?\fIclassName ...\fR? +. +This slot (see \fBSLOTTED DEFINITIONS\fR below) +sets or updates the list of additional classes that are to be mixed into +all the instances of the class being defined. Each \fIclassName\fR argument +names a single class that is to be mixed in. +By default, this slot works by replacement. +.TP +\fBrenamemethod\fI fromName toName\fR +. +This renames the method called \fIfromName\fR in a class to \fItoName\fR. The +method must have previously existed in the class, and \fItoName\fR must not +previously refer to a method in that class. Does not affect the superclasses +of the class, nor does it affect the subclasses or instances of the class +(except when they have a call chain through the class being modified). Does +not change the export status of the method; if it was exported before, it will +be afterwards. +.SH "CONFIGURING OBJECTS" .PP The following commands are supported in the \fIdefScript\fR for \fBoo::objdefine\fR, each of which may also be used in the \fIsubcommand\fR form: .TP -\fBclass\fI className\fR -. -This allows the class of an object to be changed after creation. Note that the -class's constructors are not called when this is done, and so the object may -well be in an inconsistent state unless additional configuration work is done. -.TP -\fBdeletemethod\fI name\fR ?\fIname ...\fR -. -This deletes each of the methods called \fIname\fR from an object. The methods -must have previously existed in that object. Does not affect the classes that -the object is an instance of. -.TP \fBexport\fI name \fR?\fIname ...\fR? . This arranges for each of the named methods, \fIname\fR, to be exported (i.e. usable outside the object through the object's command) by the object being defined. Note that the methods themselves may be actually defined by a class or superclass; object exports override class visibility. .TP -\fBfilter\fR ?\fI\-slotOperation\fR? ?\fImethodName ...\fR? -. -This slot (see \fBSLOTTED DEFINITIONS\fR below) -sets or updates the list of method names that are used to guard whether a -method call to the object may be called and what the method's results are. -Each \fImethodName\fR names a single filtering method (which may be exposed or -not exposed); it is not an error for a non-existent method to be named. Note -that the actual list of filters also depends on the filters set upon any -classes that the object is an instance of. -By default, this slot works by appending. -.TP \fBforward\fI name cmdName \fR?\fIarg ...\fR? . This creates or updates a forwarded object method called \fIname\fR. The method is defined be forwarded to the command called \fIcmdName\fR, with additional arguments, \fIarg\fR etc., added before those arguments specified @@ -361,23 +377,10 @@ just a private definition context. All other definition commands have no difference in behavior when used in a private definition context. .RE .VE TIP500 .TP -\fBrenamemethod\fI fromName toName\fR -. -This renames the method called \fIfromName\fR in an object to \fItoName\fR. -The method must have previously existed in the object, and \fItoName\fR must -not previously refer to a method in that object. Does not affect the classes -that the object is an instance of. Does not change the export status of the -method; if it was exported before, it will be afterwards. -.TP -\fBself \fR -.VS TIP470 -This gives the name of the object currently being configured. -.VE TIP470 -.TP \fBunexport\fI name \fR?\fIname ...\fR? . This arranges for each of the named methods, \fIname\fR, to be not exported (i.e. not usable outside the object through the object's command, but instead just through the \fBmy\fR command visible in the object's context) by the @@ -406,10 +409,50 @@ the methods of this instance object, and the name of the variable in the instance namespace has a unique prefix that makes accidental use from superclass methods extremely unlikely. .VE TIP500 .RE +.SS "ADVANCED OBJECT CONFIGURATION OPTIONS" +.PP +The following definitions are also supported, but are not required in simple +programs: +.TP +\fBclass\fI className\fR +. +This allows the class of an object to be changed after creation. Note that the +class's constructors are not called when this is done, and so the object may +well be in an inconsistent state unless additional configuration work is done. +.TP +\fBdeletemethod\fI name\fR ?\fIname ...\fR +. +This deletes each of the methods called \fIname\fR from an object. The methods +must have previously existed in that object. Does not affect the classes that +the object is an instance of. +.TP +\fBfilter\fR ?\fI\-slotOperation\fR? ?\fImethodName ...\fR? +. +This slot (see \fBSLOTTED DEFINITIONS\fR below) +sets or updates the list of method names that are used to guard whether a +method call to the object may be called and what the method's results are. +Each \fImethodName\fR names a single filtering method (which may be exposed or +not exposed); it is not an error for a non-existent method to be named. Note +that the actual list of filters also depends on the filters set upon any +classes that the object is an instance of. +By default, this slot works by appending. +.TP +\fBrenamemethod\fI fromName toName\fR +. +This renames the method called \fIfromName\fR in an object to \fItoName\fR. +The method must have previously existed in the object, and \fItoName\fR must +not previously refer to a method in that object. Does not affect the classes +that the object is an instance of. Does not change the export status of the +method; if it was exported before, it will be afterwards. +.TP +\fBself \fR +.VS TIP470 +This gives the name of the object currently being configured. +.VE TIP470 .SH "PRIVATE METHODS" .VS TIP500 When a class or instance has a private method, that private method can only be invoked from within methods of that class or instance. Other callers of the object's methods \fIcannot\fR invoke private methods, it is as if the private @@ -657,13 +700,67 @@ \fI\(-> DB: locate row ::Group with groupname=webadmins\fR $g update "emailaddress=admins" \fI\(-> DB: update row ::oo::Obj125 with emailaddress=admins\fR .CE .VE TIP478 +.PP +.VS TIP524 +This example shows how to make a custom definition for a class. Note that it +explicitly includes delegation to the existing definition commands via +\fBnamespace path\fR. +.PP +.CS +namespace eval myDefinitions { + # Delegate to existing definitions where not overridden + namespace path \fB::oo::define\fR + + # A custom type of method + proc exprmethod {name arguments body} { + tailcall \fBmethod\fR $name $arguments [list expr $body] + } + + # A custom way of building a constructor + proc parameters args { + uplevel 1 [list \fBvariable\fR {*}$args] + set body [join [lmap a $args { + string map [list VAR $a] { + set [my varname VAR] [expr {double($VAR)}] + } + }] ";"] + tailcall \fBconstructor\fR $args $body + } +} + +# Bind the namespace into a (very simple) metaclass for use +oo::class create exprclass { + \fBsuperclass\fR oo::class + \fBdefinitionnamespace\fR myDefinitions +} + +# Use the custom definitions +exprclass create quadratic { + parameters a b c + exprmethod evaluate {x} { + ($a * $x**2) + ($b * $x) + $c + } +} + +# Showing the resulting class and object in action +quadratic create quad 1 2 3 +for {set x 0} {$x <= 4} {incr x} { + puts [format "quad(%d) = %.2f" $x [quad evaluate $x]] +} + \fI\(-> quad(0) = 3.00\fR + \fI\(-> quad(1) = 6.00\fR + \fI\(-> quad(2) = 11.00\fR + \fI\(-> quad(3) = 18.00\fR + \fI\(-> quad(4) = 27.00\fR +.CE +.VE TIP524 .SH "SEE ALSO" next(n), oo::class(n), oo::object(n) .SH KEYWORDS class, definition, method, object, slot .\" Local variables: .\" mode: nroff .\" fill-column: 78 .\" End: Index: doc/info.n ================================================================== --- doc/info.n +++ doc/info.n @@ -478,10 +478,28 @@ This subcommand returns a description of the definition of the method named \fImethod\fR of class \fIclass\fR. The definition is described as a two element list; the first element is the list of arguments to the method in a form suitable for passing to another call to \fBproc\fR or a method definition, and the second element is the body of the method. +.TP +\fBinfo class definitionnamespace\fI class\fR ?\fIkind\fR? +.VS TIP524 +This subcommand returns the definition namespace for \fIkind\fR definitions of +the class \fIclass\fR; the definition namespace only affects the instances of +\fIclass\fR, not \fIclass\fR itself. The \fIkind\fR can be either +\fB\-class\fR to return the definition namespace used for \fBoo::define\fR, or +\fB\-instance\fR to return the definition namespace used for +\fBoo::objdefine\fR; the \fB\-class\fR kind is default (though this is only +actually useful on classes that are subclasses of \fBoo::class\fR). +.RS +.PP +If \fIclass\fR does not provide a definition namespace of the specified kind, +this command returns the empty string. In those circumstances, the +\fBoo::define\fR and \fBoo::objdefine\fR commands look up which definition +namespace to use using the class inheritance hierarchy. +.RE +.VE TIP524 .TP \fBinfo class destructor\fI class\fR . This subcommand returns the body of the destructor of class \fIclass\fR. If no destructor is present, this returns the empty string. Index: generic/tclOO.c ================================================================== --- generic/tclOO.c +++ generic/tclOO.c @@ -24,10 +24,11 @@ const char *name; Tcl_ObjCmdProc *objProc; int flag; } defineCmds[] = { {"constructor", TclOODefineConstructorObjCmd, 0}, + {"definitionnamespace", TclOODefineDefnNsObjCmd, 0}, {"deletemethod", TclOODefineDeleteMethodObjCmd, 0}, {"destructor", TclOODefineDestructorObjCmd, 0}, {"export", TclOODefineExportObjCmd, 0}, {"forward", TclOODefineForwardObjCmd, 0}, {"method", TclOODefineMethodObjCmd, 0}, @@ -443,10 +444,11 @@ Tcl_Interp *interp, Foundation *fPtr) { Class fakeCls; Object fakeObject; + Tcl_Obj *defNsName; /* Stand up a phony class for bootstrapping. */ fPtr->objectCls = &fakeCls; /* referenced in TclOOAllocClass to increment the refCount. */ fakeCls.thisPtr = &fakeObject; @@ -454,20 +456,29 @@ fPtr->objectCls = TclOOAllocClass(interp, AllocObject(interp, "object", (Namespace *)fPtr->ooNs, NULL)); /* Corresponding TclOODecrRefCount in KillFoudation */ AddRef(fPtr->objectCls->thisPtr); - /* This is why it is unnecessary in this routine to replace the + /* + * This is why it is unnecessary in this routine to replace the * incremented reference count of fPtr->objectCls that was swallowed by - * fakeObject. */ + * fakeObject. + */ + fPtr->objectCls->superclasses.num = 0; ckfree(fPtr->objectCls->superclasses.list); fPtr->objectCls->superclasses.list = NULL; - /* special initialization for the primordial objects */ + /* + * Special initialization for the primordial objects. + */ + fPtr->objectCls->thisPtr->flags |= ROOT_OBJECT; fPtr->objectCls->flags |= ROOT_OBJECT; + TclNewLiteralStringObj(defNsName, "::oo::objdefine"); + fPtr->objectCls->objDefinitionNs = defNsName; + Tcl_IncrRefCount(defNsName); fPtr->classCls = TclOOAllocClass(interp, AllocObject(interp, "class", (Namespace *)fPtr->ooNs, NULL)); /* Corresponding TclOODecrRefCount in KillFoudation */ AddRef(fPtr->classCls->thisPtr); @@ -478,11 +489,14 @@ * * Corresponding TclOODecrRefCount for all incremented refcounts is in * KillFoundation. */ - /* Rewire bootstrapped objects. */ + /* + * Rewire bootstrapped objects. + */ + fPtr->objectCls->thisPtr->selfCls = fPtr->classCls; AddRef(fPtr->classCls->thisPtr); TclOOAddToInstances(fPtr->objectCls->thisPtr, fPtr->classCls); fPtr->classCls->thisPtr->selfCls = fPtr->classCls; @@ -489,10 +503,13 @@ AddRef(fPtr->classCls->thisPtr); TclOOAddToInstances(fPtr->classCls->thisPtr, fPtr->classCls); fPtr->classCls->thisPtr->flags |= ROOT_CLASS; fPtr->classCls->flags |= ROOT_CLASS; + TclNewLiteralStringObj(defNsName, "::oo::define"); + fPtr->classCls->clsDefinitionNs = defNsName; + Tcl_IncrRefCount(defNsName); /* Standard initialization for new Objects */ TclOOAddToSubclasses(fPtr->classCls, fPtr->objectCls); /* @@ -955,10 +972,23 @@ } else if (IsRootObject(oPtr)) { Tcl_Panic("deleting class structure for non-deleted %s", "::oo::object"); } } + + /* + * Stop using the class for definition information. + */ + + if (clsPtr->clsDefinitionNs) { + Tcl_DecrRefCount(clsPtr->clsDefinitionNs); + clsPtr->clsDefinitionNs = NULL; + } + if (clsPtr->objDefinitionNs) { + Tcl_DecrRefCount(clsPtr->objDefinitionNs); + clsPtr->objDefinitionNs = NULL; + } /* * Squelch method implementation chain caches. */ Index: generic/tclOOCall.c ================================================================== --- generic/tclOOCall.c +++ generic/tclOOCall.c @@ -28,10 +28,26 @@ * main call chain. */ Object *oPtr; /* The object that we are building the chain * for. */ }; +/* + * Structures used for traversing the class hierarchy to find out where + * definitions are supposed to be done. + */ + +typedef struct { + Class *definerCls; + Tcl_Obj *namespaceName; +} DefineEntry; + +typedef struct { + DefineEntry *list; + int num; + int size; +} DefineChain; + /* * Extra flags used for call chain management. */ #define DEFINITE_PROTECTED 0x100000 @@ -75,10 +91,13 @@ Class *clsPtr, struct ChainBuilder *const cbPtr, Tcl_HashTable *const doneFilters, int flags); static void AddClassMethodNames(Class *clsPtr, const int flags, Tcl_HashTable *const namesPtr, Tcl_HashTable *const examinedClassesPtr); +static inline void AddDefinitionNamespaceToChain(Class *const definerCls, + Tcl_Obj *const namespaceName, + DefineChain *const definePtr, const int flags); static inline void AddMethodToCallChain(Method *const mPtr, struct ChainBuilder *const cbPtr, Tcl_HashTable *const doneFilters, Class *const filterDecl, int flags); static inline int AddInstancePrivateToCallContext(Object *const oPtr, @@ -103,10 +122,14 @@ static int AddSimpleClassChainToCallContext(Class *classPtr, Tcl_Obj *const methodNameObj, struct ChainBuilder *const cbPtr, Tcl_HashTable *const doneFilters, int flags, Class *const filterDecl); +static void AddSimpleClassDefineNamespaces(Class *classPtr, + DefineChain *const definePtr, int flags); +static inline void AddSimpleDefineNamespaces(Object *const oPtr, + DefineChain *const definePtr, int flags); static int CmpStr(const void *ptr1, const void *ptr2); static void DupMethodNameRep(Tcl_Obj *srcPtr, Tcl_Obj *dstPtr); static Tcl_NRPostProc FinalizeMethodRefs; static void FreeMethodNameRep(Tcl_Obj *objPtr); static inline int IsStillValid(CallChain *callPtr, Object *oPtr, @@ -1832,13 +1855,253 @@ resultObj = Tcl_NewListObj(callPtr->numChain, objv); TclStackFree(interp, objv); return resultObj; } + +/* + * ---------------------------------------------------------------------- + * + * TclOOGetDefineContextNamespace -- + * + * Responsible for determining which namespace to use for definitions. + * This is done by building a define chain, which models (strongly!) the + * way that a call chain works but with a different internal model. + * + * Then it walks the chain to find the first namespace name that actually + * resolves to an existing namespace. + * + * Returns: + * Name of namespace, or NULL if none can be found. Note that this + * function does *not* set an error message in the interpreter on failure. + * + * ---------------------------------------------------------------------- + */ + +#define DEFINE_CHAIN_STATIC_SIZE 4 /* Enough space to store most cases. */ + +Tcl_Namespace * +TclOOGetDefineContextNamespace( + Tcl_Interp *interp, /* In what interpreter should namespace names + * actually be resolved. */ + Object *oPtr, /* The object to get the context for. */ + int forClass) /* What sort of context are we looking for. + * If true, we are going to use this for + * [oo::define], otherwise, we are going to + * use this for [oo::objdefine]. */ +{ + DefineChain define; + DefineEntry staticSpace[DEFINE_CHAIN_STATIC_SIZE]; + DefineEntry *entryPtr; + Tcl_Namespace *nsPtr = NULL; + int i; + + define.list = staticSpace; + define.num = 0; + define.size = DEFINE_CHAIN_STATIC_SIZE; + + /* + * Add the actual define locations. We have to do this twice to handle + * class mixins right. + */ + + AddSimpleDefineNamespaces(oPtr, &define, forClass | BUILDING_MIXINS); + AddSimpleDefineNamespaces(oPtr, &define, forClass); + + /* + * Go through the list until we find a namespace whose name we can + * resolve. + */ + + FOREACH_STRUCT(entryPtr, define) { + if (TclGetNamespaceFromObj(interp, entryPtr->namespaceName, + &nsPtr) == TCL_OK) { + break; + } + Tcl_ResetResult(interp); + } + if (define.list != staticSpace) { + ckfree(define.list); + } + return nsPtr; +} + +/* + * ---------------------------------------------------------------------- + * + * AddSimpleDefineNamespaces -- + * + * Adds to the definition chain all the definitions provided by an + * object's class and its mixins, taking into account everything they + * inherit from. + * + * ---------------------------------------------------------------------- + */ + +static inline void +AddSimpleDefineNamespaces( + Object *const oPtr, /* Object to add define chain entries for. */ + DefineChain *const definePtr, + /* Where to add the define chain entries. */ + int flags) /* What sort of define chain are we + * building. */ +{ + Class *mixinPtr; + int i; + + FOREACH(mixinPtr, oPtr->mixins) { + AddSimpleClassDefineNamespaces(mixinPtr, definePtr, + flags | TRAVERSED_MIXIN); + } + + AddSimpleClassDefineNamespaces(oPtr->selfCls, definePtr, flags); +} + +/* + * ---------------------------------------------------------------------- + * + * AddSimpleClassDefineNamespaces -- + * + * Adds to the definition chain all the definitions provided by a class + * and its superclasses and its class mixins. + * + * ---------------------------------------------------------------------- + */ + +static void +AddSimpleClassDefineNamespaces( + Class *classPtr, /* Class to add the define chain entries for. */ + DefineChain *const definePtr, + /* Where to add the define chain entries. */ + int flags) /* What sort of define chain are we + * building. */ +{ + int i; + Class *superPtr; + + /* + * We hard-code the tail-recursive form. It's by far the most common case + * *and* it is much more gentle on the stack. + */ + + tailRecurse: + FOREACH(superPtr, classPtr->mixins) { + AddSimpleClassDefineNamespaces(superPtr, definePtr, + flags | TRAVERSED_MIXIN); + } + + if (flags & ~(TRAVERSED_MIXIN | BUILDING_MIXINS)) { + AddDefinitionNamespaceToChain(classPtr, classPtr->clsDefinitionNs, + definePtr, flags); + } else { + AddDefinitionNamespaceToChain(classPtr, classPtr->objDefinitionNs, + definePtr, flags); + } + + switch (classPtr->superclasses.num) { + case 1: + classPtr = classPtr->superclasses.list[0]; + goto tailRecurse; + default: + FOREACH(superPtr, classPtr->superclasses) { + AddSimpleClassDefineNamespaces(superPtr, definePtr, flags); + } + case 0: + return; + } +} + +/* + * ---------------------------------------------------------------------- + * + * AddDefinitionNamespaceToChain -- + * + * Adds a single item to the definition chain (if it is meaningful), + * reallocating the space for the chain if necessary. + * + * ---------------------------------------------------------------------- + */ + +static inline void +AddDefinitionNamespaceToChain( + Class *definerCls, /* What class defines this entry. */ + Tcl_Obj *namespaceName, /* The name for this entry (or NULL, a + * no-op). */ + DefineChain *const definePtr, + /* The define chain to add the method + * implementation to. */ + int flags) /* Used to check if we're mixin-consistent + * only. Mixin-consistent means that either + * we're looking to add things from a mixin + * and we have passed a mixin, or we're not + * looking to add things from a mixin and have + * not passed a mixin. */ +{ + int i; + + /* + * Return if this entry is blank. This is also where we enforce + * mixin-consistency. + */ + + if (namespaceName == NULL || !MIXIN_CONSISTENT(flags)) { + return; + } + + /* + * First test whether the method is already in the call chain. + */ + + for (i=0 ; inum ; i++) { + if (definePtr->list[i].definerCls == definerCls) { + /* + * Call chain semantics states that methods come as *late* in the + * call chain as possible. This is done by copying down the + * following methods. Note that this does not change the number of + * method invocations in the call chain; it just rearranges them. + * + * We skip changing anything if the place we found was already at + * the end of the list. + */ + + if (i < definePtr->num - 1) { + memmove(&definePtr->list[i], &definePtr->list[i + 1], + sizeof(DefineEntry) * (definePtr->num - i - 1)); + definePtr->list[i].definerCls = definerCls; + definePtr->list[i].namespaceName = namespaceName; + } + return; + } + } + + /* + * Need to really add the define. This is made a bit more complex by the + * fact that we are using some "static" space initially, and only start + * realloc-ing if the chain gets long. + */ + + if (definePtr->num == definePtr->size) { + definePtr->size *= 2; + if (definePtr->num == DEFINE_CHAIN_STATIC_SIZE) { + DefineEntry *staticList = definePtr->list; + + definePtr->list = + ckalloc(sizeof(DefineEntry) * definePtr->size); + memcpy(definePtr->list, staticList, + sizeof(DefineEntry) * definePtr->num); + } else { + definePtr->list = ckrealloc(definePtr->list, + sizeof(DefineEntry) * definePtr->size); + } + } + definePtr->list[i].definerCls = definerCls; + definePtr->list[i].namespaceName = namespaceName; + definePtr->num++; +} /* * Local Variables: * mode: c * c-basic-offset: 4 * fill-column: 78 * End: */ Index: generic/tclOODefineCmds.c ================================================================== --- generic/tclOODefineCmds.c +++ generic/tclOODefineCmds.c @@ -61,10 +61,12 @@ static inline int MagicDefinitionInvoke(Tcl_Interp *interp, Tcl_Namespace *nsPtr, int cmdIndex, int objc, Tcl_Obj *const *objv); static inline Class * GetClassInOuterContext(Tcl_Interp *interp, Tcl_Obj *className, const char *errMsg); +static inline Tcl_Namespace *GetNamespaceInOuterContext(Tcl_Interp *interp, + Tcl_Obj *namespaceName); static inline int InitDefineContext(Tcl_Interp *interp, Tcl_Namespace *namespacePtr, Object *oPtr, int objc, Tcl_Obj *const objv[]); static inline void RecomputeClassCacheFlag(Object *oPtr); static int RenameDeleteMethod(Tcl_Interp *interp, Object *oPtr, @@ -826,12 +828,11 @@ { CallFrame *framePtr, **framePtrPtr = &framePtr; if (namespacePtr == NULL) { Tcl_SetObjResult(interp, Tcl_NewStringObj( - "cannot process definitions; support namespace deleted", - -1)); + "no definition namespace available", -1)); Tcl_SetErrorCode(interp, "TCL", "OO", "MONKEY_BUSINESS", NULL); return TCL_ERROR; } /* @@ -886,16 +887,16 @@ } /* * ---------------------------------------------------------------------- * - * GetClassInOuterContext -- + * GetClassInOuterContext, GetNamespaceInOuterContext -- * - * Wrapper round Tcl_GetObjectFromObj to perform the lookup in the - * context that called oo::define (or equivalent). Note that this may - * have to go up multiple levels to get the level that we started doing - * definitions at. + * Wrappers round Tcl_GetObjectFromObj and TclGetNamespaceFromObj to + * perform the lookup in the context that called oo::define (or + * equivalent). Note that this may have to go up multiple levels to get + * the level that we started doing definitions at. * * ---------------------------------------------------------------------- */ static inline Class * @@ -926,10 +927,35 @@ TclGetString(className), NULL); return NULL; } return oPtr->classPtr; } + +static inline Tcl_Namespace * +GetNamespaceInOuterContext( + Tcl_Interp *interp, + Tcl_Obj *namespaceName) +{ + Interp *iPtr = (Interp *) interp; + Tcl_Namespace *nsPtr; + int result; + CallFrame *savedFramePtr = iPtr->varFramePtr; + + while (iPtr->varFramePtr->isProcCallFrame == FRAME_IS_OO_DEFINE + || iPtr->varFramePtr->isProcCallFrame == PRIVATE_FRAME) { + if (iPtr->varFramePtr->callerVarPtr == NULL) { + Tcl_Panic("getting outer context when already in global context"); + } + iPtr->varFramePtr = iPtr->varFramePtr->callerVarPtr; + } + result = TclGetNamespaceFromObj(interp, namespaceName, &nsPtr); + iPtr->varFramePtr = savedFramePtr; + if (result != TCL_OK) { + return NULL; + } + return nsPtr; +} /* * ---------------------------------------------------------------------- * * GenerateErrorInfo -- @@ -1051,11 +1077,11 @@ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { - Foundation *fPtr = TclOOGetFoundation(interp); + Tcl_Namespace *nsPtr; Object *oPtr; int result; if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "className arg ?arg ...?"); @@ -1066,11 +1092,11 @@ if (oPtr == NULL) { return TCL_ERROR; } if (oPtr->classPtr == NULL) { Tcl_SetObjResult(interp, Tcl_ObjPrintf( - "%s does not refer to a class",TclGetString(objv[1]))); + "%s does not refer to a class", TclGetString(objv[1]))); Tcl_SetErrorCode(interp, "TCL", "LOOKUP", "CLASS", TclGetString(objv[1]), NULL); return TCL_ERROR; } @@ -1077,11 +1103,12 @@ /* * Make the oo::define namespace the current namespace and evaluate the * command(s). */ - if (InitDefineContext(interp, fPtr->defineNs, oPtr, objc,objv) != TCL_OK){ + nsPtr = TclOOGetDefineContextNamespace(interp, oPtr, 1); + if (InitDefineContext(interp, nsPtr, oPtr, objc, objv) != TCL_OK) { return TCL_ERROR; } AddRef(oPtr); if (objc == 3) { @@ -1093,11 +1120,11 @@ if (result == TCL_ERROR) { GenerateErrorInfo(interp, oPtr, objNameObj, "class"); } TclDecrRefCount(objNameObj); } else { - result = MagicDefinitionInvoke(interp, fPtr->defineNs, 2, objc, objv); + result = MagicDefinitionInvoke(interp, nsPtr, 2, objc, objv); } TclOODecrRefCount(oPtr); /* * Restore the previous "current" namespace. @@ -1126,11 +1153,11 @@ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { - Foundation *fPtr = TclOOGetFoundation(interp); + Tcl_Namespace *nsPtr; Object *oPtr; int result; if (objc < 3) { Tcl_WrongNumArgs(interp, 1, objv, "objectName arg ?arg ...?"); @@ -1145,11 +1172,12 @@ /* * Make the oo::objdefine namespace the current namespace and evaluate the * command(s). */ - if (InitDefineContext(interp, fPtr->objdefNs, oPtr, objc,objv) != TCL_OK){ + nsPtr = TclOOGetDefineContextNamespace(interp, oPtr, 0); + if (InitDefineContext(interp, nsPtr, oPtr, objc, objv) != TCL_OK) { return TCL_ERROR; } AddRef(oPtr); if (objc == 3) { @@ -1161,11 +1189,11 @@ if (result == TCL_ERROR) { GenerateErrorInfo(interp, oPtr, objNameObj, "object"); } TclDecrRefCount(objNameObj); } else { - result = MagicDefinitionInvoke(interp, fPtr->objdefNs, 2, objc, objv); + result = MagicDefinitionInvoke(interp, nsPtr, 2, objc, objv); } TclOODecrRefCount(oPtr); /* * Restore the previous "current" namespace. @@ -1194,11 +1222,11 @@ ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv) { - Foundation *fPtr = TclOOGetFoundation(interp); + Tcl_Namespace *nsPtr; Object *oPtr; int result, private; oPtr = (Object *) TclOOGetDefineCmdContext(interp); if (oPtr == NULL) { @@ -1215,11 +1243,12 @@ /* * Make the oo::objdefine namespace the current namespace and evaluate the * command(s). */ - if (InitDefineContext(interp, fPtr->objdefNs, oPtr, objc,objv) != TCL_OK){ + nsPtr = TclOOGetDefineContextNamespace(interp, oPtr, 0); + if (InitDefineContext(interp, nsPtr, oPtr, objc, objv) != TCL_OK) { return TCL_ERROR; } if (private) { ((Interp *) interp)->varFramePtr->isProcCallFrame = PRIVATE_FRAME; } @@ -1234,11 +1263,11 @@ if (result == TCL_ERROR) { GenerateErrorInfo(interp, oPtr, objNameObj, "class object"); } TclDecrRefCount(objNameObj); } else { - result = MagicDefinitionInvoke(interp, fPtr->objdefNs, 1, objc, objv); + result = MagicDefinitionInvoke(interp, nsPtr, 1, objc, objv); } TclOODecrRefCount(oPtr); /* * Restore the previous "current" namespace. @@ -1528,10 +1557,95 @@ */ Tcl_ClassSetConstructor(interp, (Tcl_Class) clsPtr, method); return TCL_OK; } + +/* + * ---------------------------------------------------------------------- + * + * TclOODefineDefnNsObjCmd -- + * + * Implementation of the "definitionnamespace" subcommand of the + * "oo::define" command. + * + * ---------------------------------------------------------------------- + */ + +int +TclOODefineDefnNsObjCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const *objv) +{ + static const char *kindList[] = { + "-class", + "-instance", + NULL + }; + int kind = 0; + Object *oPtr; + Tcl_Namespace *nsPtr; + Tcl_Obj *nsNamePtr, **storagePtr; + + oPtr = (Object *) TclOOGetDefineCmdContext(interp); + if (oPtr == NULL) { + return TCL_ERROR; + } + if (!oPtr->classPtr) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "attempt to misuse API", -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "MONKEY_BUSINESS", NULL); + return TCL_ERROR; + } + if (oPtr->flags & (ROOT_OBJECT | ROOT_CLASS)) { + Tcl_SetObjResult(interp, Tcl_NewStringObj( + "may not modify the definition namespace of the root classes", + -1)); + Tcl_SetErrorCode(interp, "TCL", "OO", "MONKEY_BUSINESS", NULL); + return TCL_ERROR; + } + + /* + * Parse the arguments and work out what the user wants to do. + */ + + if (objc != 2 && objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "?kind? namespace"); + return TCL_ERROR; + } + if (objc == 3 && Tcl_GetIndexFromObj(interp, objv[1], kindList, "kind", 0, + &kind) != TCL_OK) { + return TCL_ERROR; + } + if (!Tcl_GetString(objv[objc - 1])[0]) { + nsNamePtr = NULL; + } else { + nsPtr = GetNamespaceInOuterContext(interp, objv[objc - 1]); + if (nsPtr == NULL) { + return TCL_ERROR; + } + nsNamePtr = Tcl_NewStringObj(nsPtr->fullName, -1); + Tcl_IncrRefCount(nsNamePtr); + } + + /* + * Update the correct field of the class definition. + */ + + if (kind) { + storagePtr = &oPtr->classPtr->objDefinitionNs; + } else { + storagePtr = &oPtr->classPtr->clsDefinitionNs; + } + if (*storagePtr != NULL) { + Tcl_DecrRefCount(*storagePtr); + } + *storagePtr = nsNamePtr; + return TCL_OK; +} /* * ---------------------------------------------------------------------- * * TclOODefineDeleteMethodObjCmd -- Index: generic/tclOOInfo.c ================================================================== --- generic/tclOOInfo.c +++ generic/tclOOInfo.c @@ -31,10 +31,11 @@ static Tcl_ObjCmdProc InfoObjectVarsCmd; static Tcl_ObjCmdProc InfoObjectVariablesCmd; static Tcl_ObjCmdProc InfoClassCallCmd; static Tcl_ObjCmdProc InfoClassConstrCmd; static Tcl_ObjCmdProc InfoClassDefnCmd; +static Tcl_ObjCmdProc InfoClassDefnNsCmd; static Tcl_ObjCmdProc InfoClassDestrCmd; static Tcl_ObjCmdProc InfoClassFiltersCmd; static Tcl_ObjCmdProc InfoClassForwardCmd; static Tcl_ObjCmdProc InfoClassInstancesCmd; static Tcl_ObjCmdProc InfoClassMethodsCmd; @@ -71,10 +72,11 @@ static const EnsembleImplMap infoClassCmds[] = { {"call", InfoClassCallCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, {"constructor", InfoClassConstrCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, {"definition", InfoClassDefnCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, + {"definitionnamespace", InfoClassDefnNsCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0}, {"destructor", InfoClassDestrCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, {"filters", InfoClassFiltersCmd, TclCompileBasic1ArgCmd, NULL, NULL, 0}, {"forward", InfoClassForwardCmd, TclCompileBasic2ArgCmd, NULL, NULL, 0}, {"instances", InfoClassInstancesCmd, TclCompileBasic1Or2ArgCmd, NULL, NULL, 0}, {"methods", InfoClassMethodsCmd, TclCompileBasicMin1ArgCmd, NULL, NULL, 0}, @@ -1027,10 +1029,60 @@ } resultObjs[1] = TclOOGetMethodBody(Tcl_GetHashValue(hPtr)); Tcl_SetObjResult(interp, Tcl_NewListObj(2, resultObjs)); return TCL_OK; } + +/* + * ---------------------------------------------------------------------- + * + * InfoClassDefnNsCmd -- + * + * Implements [info class definitionnamespace $clsName ?$kind?] + * + * ---------------------------------------------------------------------- + */ + +static int +InfoClassDefnNsCmd( + ClientData clientData, + Tcl_Interp *interp, + int objc, + Tcl_Obj *const objv[]) +{ + static const char *kindList[] = { + "-class", + "-instance", + NULL + }; + int kind = 0; + Tcl_Obj *nsNamePtr; + Class *clsPtr; + + if (objc != 2 && objc != 3) { + Tcl_WrongNumArgs(interp, 1, objv, "className ?kind?"); + return TCL_ERROR; + } + clsPtr = GetClassFromObj(interp, objv[1]); + if (clsPtr == NULL) { + return TCL_ERROR; + } + if (objc == 3 && Tcl_GetIndexFromObj(interp, objv[2], kindList, "kind", 0, + &kind) != TCL_OK) { + return TCL_ERROR; + } + + if (kind) { + nsNamePtr = clsPtr->objDefinitionNs; + } else { + nsNamePtr = clsPtr->clsDefinitionNs; + } + if (nsNamePtr) { + Tcl_SetObjResult(interp, nsNamePtr); + } + return TCL_OK; +} /* * ---------------------------------------------------------------------- * * InfoClassDestrCmd -- Index: generic/tclOOInt.h ================================================================== --- generic/tclOOInt.h +++ generic/tclOOInt.h @@ -302,10 +302,28 @@ * when getting method chains). */ VariableNameList variables; PrivateVariableList privateVariables; /* Configurations for the variable resolver * used inside methods. */ + Tcl_Obj *clsDefinitionNs; /* Name of the namespace to use for + * definitions commands of instances of this + * class in when those instances are defined + * as classes. If NULL, use the value from the + * class hierarchy. It's an error at + * [oo::define] call time if this namespace is + * defined but doesn't exist; we also check at + * setting time but don't check between + * times. */ + Tcl_Obj *objDefinitionNs; /* Name of the namespace to use for + * definitions commands of instances of this + * class in when those instances are defined + * as instances. If NULL, use the value from + * the class hierarchy. It's an error at + * [oo::objdefine]/[self] call time if this + * namespace is defined but doesn't exist; we + * also check at setting time but don't check + * between times. */ } Class; /* * The foundation of the object system within an interpreter contains * references to the key classes and namespaces, together with a few other @@ -438,10 +456,13 @@ MODULE_SCOPE int TclOOObjDefObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); MODULE_SCOPE int TclOODefineConstructorObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, + Tcl_Obj *const *objv); +MODULE_SCOPE int TclOODefineDefnNsObjCmd(ClientData clientData, + Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); MODULE_SCOPE int TclOODefineDeleteMethodObjCmd(ClientData clientData, Tcl_Interp *interp, int objc, Tcl_Obj *const *objv); MODULE_SCOPE int TclOODefineDestructorObjCmd(ClientData clientData, @@ -551,10 +572,12 @@ MODULE_SCOPE void TclOODelMethodRef(Method *method); MODULE_SCOPE CallContext *TclOOGetCallContext(Object *oPtr, Tcl_Obj *methodNameObj, int flags, Object *contextObjPtr, Class *contextClsPtr, Tcl_Obj *cacheInThisObj); +MODULE_SCOPE Tcl_Namespace *TclOOGetDefineContextNamespace( + Tcl_Interp *interp, Object *oPtr, int forClass); MODULE_SCOPE CallChain *TclOOGetStereotypeCallChain(Class *clsPtr, Tcl_Obj *methodNameObj, int flags); MODULE_SCOPE Foundation *TclOOGetFoundation(Tcl_Interp *interp); MODULE_SCOPE Tcl_Obj * TclOOGetFwdFromMethod(Method *mPtr); MODULE_SCOPE Proc * TclOOGetProcFromMethod(Method *mPtr); Index: generic/tclOOScript.h ================================================================== --- generic/tclOOScript.h +++ generic/tclOOScript.h @@ -96,13 +96,13 @@ "\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 superclass -append $d\n" +"\t\t\tdefine $delegate ::oo::define::superclass -append $d\n" "\t\t}\n" -"\t\tobjdefine $class mixin -append $delegate\n" +"\t\tobjdefine $class ::oo::objdefine::mixin -append $delegate\n" "\t}\n" "\tproc UpdateClassDelegatesAfterClone {originObject targetObject} {\n" "\t\tset originDelegate [DelegateName $originObject]\n" "\t\tset targetDelegate [DelegateName $targetObject]\n" "\t\tif {\n" Index: generic/tclOOScript.tcl ================================================================== --- generic/tclOOScript.tcl +++ generic/tclOOScript.tcl @@ -151,13 +151,13 @@ foreach c [info class superclass $class] { set d [DelegateName $c] if {![info object isa class $d]} { continue } - define $delegate superclass -append $d + define $delegate ::oo::define::superclass -append $d } - objdefine $class mixin -append $delegate + objdefine $class ::oo::objdefine::mixin -append $delegate } # ---------------------------------------------------------------------- # # UpdateClassDelegatesAfterClone -- @@ -174,11 +174,11 @@ if { [info object isa class $originDelegate] && ![info object isa class $targetDelegate] } then { copy $originDelegate $targetDelegate - objdefine $targetObject mixin -set \ + objdefine $targetObject ::oo::objdefine::mixin -set \ {*}[lmap c [info object mixin $targetObject] { if {$c eq $originDelegate} {set targetDelegate} {set c} }] } } Index: tests/oo.test ================================================================== --- tests/oo.test +++ tests/oo.test @@ -327,20 +327,21 @@ } -result {} test oo-1.21 {basic test of OO functionality: default relations} -setup { set fresh [interp create] } -body { lmap x [$fresh eval { + set initials {::oo::object ::oo::class ::oo::Slot} foreach cmd {instances subclasses mixins superclass} { - foreach initial {object class Slot} { - lappend x [info class $cmd ::oo::$initial] + foreach initial $initials { + lappend x [info class $cmd $initial] } } - foreach initial {object class Slot} { - lappend x [info object class ::oo::$initial] + foreach initial $initials { + lappend x [info object class $initial] } return $x - }] {lsort [lmap y $x {if {[string match *::delegate $y]} continue; set y}]} + }] {lsort [lsearch -all -not -inline $x *::delegate]} } -cleanup { interp delete $fresh } -result {{} {::oo::Slot ::oo::abstract ::oo::class ::oo::object ::oo::singleton} {::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::abstract ::oo::singleton} {} {} {} {} {} ::oo::object ::oo::object ::oo::class ::oo::class ::oo::class} test oo-2.1 {basic test of OO functionality: constructor} -setup { @@ -2517,11 +2518,11 @@ } -returnCodes 1 -cleanup { foo destroy } -result {"foo" is not a class} test oo-17.4 {OO: class introspection} -body { info class gorp oo::object -} -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be call, constructor, definition, destructor, filters, forward, instances, methods, methodtype, mixins, subclasses, superclasses, or variables} +} -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be call, constructor, definition, definitionnamespace, destructor, filters, forward, instances, methods, methodtype, mixins, subclasses, superclasses, or variables} test oo-17.5 {OO: class introspection} -setup { oo::class create testClass } -body { testClass create foo testClass create bar @@ -5074,12 +5075,260 @@ cls1 create x lappend result [catch {cls1 Hi}] [x poke] } -cleanup { parent destroy } -result {1 {this is ::cls1}} + +test oo-42.1 {TIP 524: definition namespace control: introspection} { + info class definitionnamespace oo::object +} {} +test oo-42.2 {TIP 524: definition namespace control: introspection} { + info class definitionnamespace oo::object -class +} {} +test oo-42.3 {TIP 524: definition namespace control: introspection} { + info class definitionnamespace oo::object -instance +} ::oo::objdefine +test oo-42.4 {TIP 524: definition namespace control: introspection} -body { + info class definitionnamespace oo::object -gorp +} -returnCodes error -result {bad kind "-gorp": must be -class or -instance} +test oo-42.5 {TIP 524: definition namespace control: introspection} -body { + info class definitionnamespace oo::object -class x +} -returnCodes error -result {wrong # args: should be "info class definitionnamespace className ?kind?"} +test oo-42.6 {TIP 524: definition namespace control: introspection} { + info class definitionnamespace oo::class +} ::oo::define +test oo-42.7 {TIP 524: definition namespace control: introspection} { + info class definitionnamespace oo::class -class +} ::oo::define +test oo-42.8 {TIP 524: definition namespace control: introspection} { + info class definitionnamespace oo::class -instance +} {} + +test oo-43.1 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef { + proc sparkle {} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace foodef + } + oo::class create foo { + superclass parent + self class foocls + } + oo::define foo { + sparkle + } +} -cleanup { + parent destroy + namespace delete foodef +} -result ok +test oo-43.2 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} + unset -nocomplain ::result +} -body { + namespace eval foodef { + namespace path ::oo::define + proc sparkle {} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace foodef + } + foocls create foo { + superclass parent + lappend ::result [sparkle] + } + return $result +} -cleanup { + parent destroy + namespace delete foodef +} -result ok +test oo-43.3 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} + unset -nocomplain ::result +} -body { + namespace eval foodef { + namespace path ::oo::define + proc sparkle {} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace -class foodef + } + foocls create foo { + superclass parent + lappend ::result [sparkle] + } + return $result +} -cleanup { + parent destroy + namespace delete foodef +} -result ok +test oo-43.4 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef { + namespace path ::oo::objdefine + proc sparkle {} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace -instance foodef + } + foocls create foo { + sparkle + } +} -returnCodes error -cleanup { + parent destroy + namespace delete foodef +} -result {invalid command name "sparkle"} +test oo-43.5 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef { + namespace path ::oo::objdefine + proc sparkle {} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace foodef + } + namespace delete foodef + foocls create foo { + sparkle + } +} -returnCodes error -cleanup { + parent destroy + catch {namespace delete foodef} +} -result {invalid command name "sparkle"} +test oo-43.6 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} + unset -nocomplain result +} -body { + namespace eval foodef { + namespace path ::oo::objdefine + proc sparkle {} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace foodef + } + foocls create foo + lappend result [catch {oo::define foo sparkle} msg] $msg + namespace delete foodef + lappend result [catch {oo::define foo sparkle} msg] $msg + namespace eval foodef { + namespace path ::oo::objdefine + proc sparkle {} {return ok} + } + lappend result [catch {oo::define foo sparkle} msg] $msg +} -cleanup { + parent destroy + catch {namespace delete foodef} +} -result {0 ok 1 {invalid command name "sparkle"} 0 ok} +test oo-43.7 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef { + namespace path ::oo::define + proc sparkle {x} {return ok} + } + oo::class create foocls { + superclass oo::class parent + definitionnamespace foodef + } + foocls create foo { + superclass parent + } + oo::define foo spar gorp +} -cleanup { + parent destroy + namespace delete foodef +} -result ok +test oo-43.8 {TIP 524: definition namespace control: semantics} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef { + namespace path ::oo::objdefine + proc sparkle {} {return ok} + } + oo::class create foo { + superclass parent + definitionnamespace -instance foodef + } + oo::objdefine [foo new] { + method x y z + sparkle + } +} -cleanup { + parent destroy + namespace delete foodef +} -result ok +test oo-43.9 {TIP 524: definition namespace control: syntax} -body { + oo::class create foo { + definitionnamespace -gorp foodef + } +} -returnCodes error -result {bad kind "-gorp": must be -class or -instance} +test oo-43.10 {TIP 524: definition namespace control: syntax} -body { + oo::class create foo { + definitionnamespace -class foodef x + } +} -returnCodes error -result {wrong # args: should be "definitionnamespace ?kind? namespace"} +test oo-43.11 {TIP 524: definition namespace control: syntax} -setup { + catch {namespace delete ::no_such_ns} +} -body { + oo::class create foo { + definitionnamespace -class ::no_such_ns + } +} -returnCodes error -result {namespace "::no_such_ns" not found} +test oo-43.12 {TIP 524: definition namespace control: user-level introspection} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef {} + oo::class create foo { + superclass oo::class parent + } + list [info class definitionnamespace foo] \ + [oo::define foo definitionnamespace foodef] \ + [info class definitionnamespace foo] \ + [oo::define foo definitionnamespace {}] \ + [info class definitionnamespace foo] +} -cleanup { + parent destroy + namespace delete foodef +} -result {{} {} ::foodef {} {}} +test oo-43.13 {TIP 524: definition namespace control: user-level introspection} -setup { + oo::class create parent + namespace eval foodef {} +} -body { + namespace eval foodef {} + oo::class create foo { + superclass parent + } + list [info class definitionnamespace foo -instance] \ + [oo::define foo definitionnamespace -instance foodef] \ + [info class definitionnamespace foo -instance] \ + [oo::define foo definitionnamespace -instance {}] \ + [info class definitionnamespace foo -instance] +} -cleanup { + parent destroy + namespace delete foodef +} -result {{} {} ::foodef {} {}} cleanupTests return # Local Variables: # mode: tcl # End: