Attachment "320.patch" to
ticket [2005460fff]
added by
dkf
2008-06-29 06:27:34.
Index: benchmarks/cps.tcl
===================================================================
RCS file: /cvsroot/tcl/oocore/benchmarks/cps.tcl,v
retrieving revision 1.4
diff -w -u -r1.4 cps.tcl
--- benchmarks/cps.tcl 25 May 2008 11:29:38 -0000 1.4
+++ benchmarks/cps.tcl 28 Jun 2008 23:23:20 -0000
@@ -74,4 +74,25 @@
puts "Combined inherited microbenchmark"
cps {boo create ::f;f bar;f destroy}
+puts "Local variable access microbenchmark"
+object create varAccess
+objdefine varAccess method countClassic {} {
+ variable count
+ incr count
+}
+cps {varAccess countClassic}
+objdefine varAccess method count257 {} {
+ my variable count
+ incr count
+}
+cps {varAccess count257}
+objdefine varAccess {
+ variable count
+ method count320 {} {
+ incr count
+ }
+}
+cps {varAccess count320}
+varAccess destroy
+
foo destroy
Index: doc/define.n
===================================================================
RCS file: /cvsroot/tcl/oocore/doc/define.n,v
retrieving revision 1.12
diff -w -u -r1.12 define.n
--- doc/define.n 16 May 2008 14:21:58 -0000 1.12
+++ doc/define.n 28 Jun 2008 23:23:21 -0000
@@ -157,6 +157,20 @@
context) by the class being defined. Note that the methods themselves may be
actually defined by a superclass; subclass unexports override superclass
visibility, and may be overridden by instance unexports.
+.TP
+\fBvariable\fR ?\fIname ...\fR?
+.VS
+This arranges for each of the named variables to be automatically made
+available in the methods, constructor and destructor declared by the class
+being defined. Note that the list of variable names is the whole list of
+variable names for the class. Each variable name must not have any namespace
+separators and must not look like an array access. All variables will be
+actually present in the instance object on which the method is executed. Note
+that the variable lists declared by a superclass or subclass are completely
+disjoint, as are variable lists declared by instances; the list of variable
+names is just for methods (and constructors and destructors) declared by this
+class.
+.VE
.SS "CONFIGURING OBJECTS"
.PP
The following commands are supported in the \fIdefScript\fR for
@@ -233,6 +247,18 @@
just through the \fBmy\fR command visible in the object's context) by the
object being defined. Note that the methods themselves may be actually defined
by a class; instance unexports override class visibility.
+.TP
+\fBvariable\fR ?\fIname ...\fR?
+.VS
+This arranges for each of the named variables to be automatically made
+available in the methods declared by the object being defined. Note that the
+list of variable names is the whole list of variable names for the object.
+Each variable name must not have any namespace separators and must not look
+like an array access. All variables will be actually present in the object on
+which the method is executed. Note that the variable lists declared by the
+classes and mixins of which the object is an instance are completely disjoint;
+the list of variable names is just for methods declared by this object.
+.VE
.SH EXAMPLES
This example demonstrates how to use both forms of the \fBoo::define\fR and
\fBoo::objdefine\fR commands (they work in the same way), as well as
Index: doc/ooInfo.n
===================================================================
RCS file: /cvsroot/tcl/oocore/doc/ooInfo.n,v
retrieving revision 1.6
diff -w -u -r1.6 ooInfo.n
--- doc/ooInfo.n 29 May 2008 09:33:19 -0000 1.6
+++ doc/ooInfo.n 28 Jun 2008 23:23:21 -0000
@@ -112,6 +112,13 @@
This subcommand returns a list of all classes that have been mixed into the
object named \fIobject\fR.
.TP
+\fBinfo object variables\fI object\fR
+.VS
+This subcommand returns a list of all variables that have been declared for
+the object named \fIobject\fR (i.e. that are automatically present in the
+object's methods).
+.VE
+.TP
\fBinfo object vars\fI object\fR ?\fIpattern\fR?
.
This subcommand returns a list of all variables in the private namespace of
@@ -195,6 +202,13 @@
.
This subcommand returns a list of direct superclasses of class \fIclass\fR in
inheritance precedence order.
+.TP
+\fBinfo class variables\fI class\fR
+.VS
+This subcommand returns a list of all variables that have been declared for
+the class named \Iclass\fR (i.e. that are automatically present in the
+class's methods, constructor and destructor).
+.VE
.SH "FUTURE CHANGES"
Note that these commands are likely to be renamed in the future.
.SH EXAMPLES
@@ -231,7 +245,7 @@
}
.CE
.SH "SEE ALSO"
-oo::class(n), oo::object(n), self(n)
+oo::class(n), oo::define(n), oo::object(n), self(n)
.SH KEYWORDS
introspection, object
Index: generic/tclOO.c
===================================================================
RCS file: /cvsroot/tcl/oocore/generic/tclOO.c,v
retrieving revision 1.58
diff -w -u -r1.58 tclOO.c
--- generic/tclOO.c 26 Jun 2008 14:54:25 -0000 1.58
+++ generic/tclOO.c 28 Jun 2008 23:23:24 -0000
@@ -38,6 +38,7 @@
{"self", TclOODefineSelfObjCmd, 0},
{"superclass", TclOODefineSuperclassObjCmd, 0},
{"unexport", TclOODefineUnexportObjCmd, 0},
+ {"variable", TclOODefineVariablesObjCmd, 0},
{NULL, NULL, 0}
}, objdefCmds[] = {
{"class", TclOODefineClassObjCmd, 1},
@@ -49,6 +50,7 @@
{"mixin", TclOODefineMixinObjCmd, 1},
{"renamemethod", TclOODefineRenameMethodObjCmd, 1},
{"unexport", TclOODefineUnexportObjCmd, 1},
+ {"variable", TclOODefineVariablesObjCmd, 1},
{NULL, NULL, 0}
};
@@ -459,6 +461,7 @@
configNamespace:
TclSetNsPath((Namespace *) oPtr->namespacePtr, 1, &fPtr->helpersNs);
+ TclOOSetupVariableResolver(oPtr->namespacePtr);
/*
* Fill in the rest of the non-zero/NULL parts of the structure.
@@ -775,7 +778,7 @@
FOREACH_HASH_DECLS;
Class *clsPtr = oPtr->classPtr, *mixinPtr;
Method *mPtr;
- Tcl_Obj *filterObj;
+ Tcl_Obj *filterObj, *variableObj;
int i, preserved = !(oPtr->flags & OBJECT_DELETED);
/*
@@ -822,6 +825,13 @@
ckfree((char *) oPtr->methodsPtr);
}
+ FOREACH(variableObj, oPtr->variables) {
+ Tcl_DecrRefCount(variableObj);
+ }
+ if (i) {
+ ckfree((char *) oPtr->variables.list);
+ }
+
if (oPtr->chainCache) {
TclOODeleteChainCache(oPtr->chainCache);
}
@@ -900,6 +910,14 @@
Tcl_DeleteHashTable(&clsPtr->classMethods);
TclOODelMethodRef(clsPtr->constructorPtr);
TclOODelMethodRef(clsPtr->destructorPtr);
+
+ FOREACH(variableObj, clsPtr->variables) {
+ Tcl_DecrRefCount(variableObj);
+ }
+ if (i) {
+ ckfree((char *) clsPtr->variables.list);
+ }
+
DelRef(clsPtr);
}
Index: generic/tclOODefineCmds.c
===================================================================
RCS file: /cvsroot/tcl/oocore/generic/tclOODefineCmds.c,v
retrieving revision 1.18
diff -w -u -r1.18 tclOODefineCmds.c
--- generic/tclOODefineCmds.c 25 May 2008 11:29:39 -0000 1.18
+++ generic/tclOODefineCmds.c 28 Jun 2008 23:23:24 -0000
@@ -1792,6 +1792,100 @@
return TCL_OK;
}
+/*
+ * ----------------------------------------------------------------------
+ *
+ * TclOODefineVariablesObjCmd --
+ * Implementation of the "variable" subcommand of the "oo::define" and
+ * "oo::objdefine" commands.
+ *
+ * ----------------------------------------------------------------------
+ */
+
+int
+TclOODefineVariablesObjCmd(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const *objv)
+{
+ int isInstanceVars = (clientData != NULL);
+ Object *oPtr = (Object *) TclOOGetDefineCmdContext(interp);
+ Tcl_Obj *variableObj;
+ int i;
+
+ if (oPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (!isInstanceVars && !oPtr->classPtr) {
+ Tcl_AppendResult(interp, "attempt to misuse API", NULL);
+ return TCL_ERROR;
+ }
+
+ for (i=1 ; i<objc ; i++) {
+ const char *varName = Tcl_GetString(objv[i]);
+
+ if (strstr(varName, "::") != NULL) {
+ Tcl_AppendResult(interp, "invalid declared variable name \"",
+ varName, "\": must not contain namespace separators",
+ NULL);
+ return TCL_ERROR;
+ }
+ if (Tcl_StringMatch(varName, "*(*)")) {
+ Tcl_AppendResult(interp, "invalid declared variable name \"",
+ varName, "\": must not refer to an array element", NULL);
+ return TCL_ERROR;
+ }
+ }
+ for (i=1 ; i<objc ; i++) {
+ Tcl_IncrRefCount(objv[i]);
+ }
+
+ if (!isInstanceVars) {
+ FOREACH(variableObj, oPtr->classPtr->variables) {
+ Tcl_DecrRefCount(variableObj);
+ }
+ if (i != objc-1) {
+ if (objc == 1) {
+ ckfree((char *) oPtr->classPtr->variables.list);
+ } else if (i) {
+ oPtr->classPtr->variables.list = (Tcl_Obj **)
+ ckrealloc((char *) oPtr->classPtr->variables.list,
+ sizeof(Tcl_Obj *) * (objc-1));
+ } else {
+ oPtr->classPtr->variables.list = (Tcl_Obj **)
+ ckalloc(sizeof(Tcl_Obj *) * (objc-1));
+ }
+ }
+ if (objc > 1) {
+ memcpy(oPtr->classPtr->variables.list, objv+1,
+ sizeof(Tcl_Obj *) * (objc-1));
+ }
+ oPtr->classPtr->variables.num = objc-1;
+ } else {
+ FOREACH(variableObj, oPtr->variables) {
+ Tcl_DecrRefCount(variableObj);
+ }
+ if (i != objc-1) {
+ if (objc == 1) {
+ ckfree((char *) oPtr->variables.list);
+ } else if (i) {
+ oPtr->variables.list = (Tcl_Obj **)
+ ckrealloc((char *) oPtr->variables.list,
+ sizeof(Tcl_Obj *) * (objc-1));
+ } else {
+ oPtr->variables.list = (Tcl_Obj **)
+ ckalloc(sizeof(Tcl_Obj *) * (objc-1));
+ }
+ }
+ if (objc > 1) {
+ memcpy(oPtr->variables.list, objv+1, sizeof(Tcl_Obj *)*(objc-1));
+ }
+ oPtr->variables.num = objc-1;
+ }
+ return TCL_OK;
+}
+
void
Tcl_ClassSetConstructor(
Tcl_Interp *interp,
Index: generic/tclOOInfo.c
===================================================================
RCS file: /cvsroot/tcl/oocore/generic/tclOOInfo.c,v
retrieving revision 1.14
diff -w -u -r1.14 tclOOInfo.c
--- generic/tclOOInfo.c 29 May 2008 09:33:19 -0000 1.14
+++ generic/tclOOInfo.c 28 Jun 2008 23:23:25 -0000
@@ -26,6 +26,7 @@
static Tcl_ObjCmdProc InfoObjectMethodsCmd;
static Tcl_ObjCmdProc InfoObjectMixinsCmd;
static Tcl_ObjCmdProc InfoObjectVarsCmd;
+static Tcl_ObjCmdProc InfoObjectVariablesCmd;
static Tcl_ObjCmdProc InfoClassConstrCmd;
static Tcl_ObjCmdProc InfoClassDefnCmd;
static Tcl_ObjCmdProc InfoClassDestrCmd;
@@ -36,6 +37,7 @@
static Tcl_ObjCmdProc InfoClassMixinsCmd;
static Tcl_ObjCmdProc InfoClassSubsCmd;
static Tcl_ObjCmdProc InfoClassSupersCmd;
+static Tcl_ObjCmdProc InfoClassVariablesCmd;
struct NameProcMap { const char *name; Tcl_ObjCmdProc *proc; };
@@ -51,6 +53,7 @@
{"::oo::InfoObject::isa", InfoObjectIsACmd},
{"::oo::InfoObject::methods", InfoObjectMethodsCmd},
{"::oo::InfoObject::mixins", InfoObjectMixinsCmd},
+ {"::oo::InfoObject::variables", InfoObjectVariablesCmd},
{"::oo::InfoObject::vars", InfoObjectVarsCmd},
{NULL, NULL}
};
@@ -70,6 +73,7 @@
{"::oo::InfoClass::mixins", InfoClassMixinsCmd},
{"::oo::InfoClass::subclasses", InfoClassSubsCmd},
{"::oo::InfoClass::superclasses", InfoClassSupersCmd},
+ {"::oo::InfoClass::variables", InfoClassVariablesCmd},
{NULL, NULL}
};
@@ -268,18 +272,8 @@
}
}
Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), argsObj);
-
- /*
- * This is copied from the [info body] implementation. See the comments
- * there for why this copy has to be done here.
- */
-
- if (procPtr->bodyPtr->bytes == NULL) {
- (void) Tcl_GetString(procPtr->bodyPtr);
- }
Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(procPtr->bodyPtr->bytes,
- procPtr->bodyPtr->length));
+ TclOOGetMethodBody(Tcl_GetHashValue(hPtr)));
return TCL_OK;
}
@@ -617,6 +611,42 @@
/*
* ----------------------------------------------------------------------
*
+ * InfoObjectVariablesCmd --
+ *
+ * Implements [info object variables $objName]
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static int
+InfoObjectVariablesCmd(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const objv[])
+{
+ Object *oPtr;
+ Tcl_Obj *variableObj;
+ int i;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "objName");
+ return TCL_ERROR;
+ }
+ oPtr = (Object *) Tcl_GetObjectFromObj(interp, objv[1]);
+ if (oPtr == NULL) {
+ return TCL_ERROR;
+ }
+
+ FOREACH(variableObj, oPtr->variables) {
+ Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), variableObj);
+ }
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* InfoObjectVarsCmd --
*
* Implements [info object vars $objName ?$pattern?]
@@ -739,12 +769,8 @@
}
}
Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), argsObj);
- if (procPtr->bodyPtr->bytes == NULL) {
- (void) Tcl_GetString(procPtr->bodyPtr);
- }
Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(procPtr->bodyPtr->bytes,
- procPtr->bodyPtr->length));
+ TclOOGetMethodBody(clsPtr->constructorPtr));
return TCL_OK;
}
@@ -816,12 +842,8 @@
}
}
Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), argsObj);
- if (procPtr->bodyPtr->bytes == NULL) {
- (void) Tcl_GetString(procPtr->bodyPtr);
- }
Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(procPtr->bodyPtr->bytes,
- procPtr->bodyPtr->length));
+ TclOOGetMethodBody(Tcl_GetHashValue(hPtr)));
return TCL_OK;
}
@@ -871,12 +893,7 @@
return TCL_ERROR;
}
- if (procPtr->bodyPtr->bytes == NULL) {
- (void) Tcl_GetString(procPtr->bodyPtr);
- }
- Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp),
- Tcl_NewStringObj(procPtr->bodyPtr->bytes,
- procPtr->bodyPtr->length));
+ Tcl_SetObjResult(interp, TclOOGetMethodBody(clsPtr->destructorPtr));
return TCL_OK;
}
@@ -1263,6 +1280,49 @@
}
/*
+ * ----------------------------------------------------------------------
+ *
+ * InfoClassVariablesCmd --
+ *
+ * Implements [info class variables $clsName]
+ *
+ * ----------------------------------------------------------------------
+ */
+
+static int
+InfoClassVariablesCmd(
+ ClientData clientData,
+ Tcl_Interp *interp,
+ int objc,
+ Tcl_Obj *const objv[])
+{
+ Object *oPtr;
+ Class *clsPtr;
+ Tcl_Obj *variableObj;
+ int i;
+
+ if (objc != 2) {
+ Tcl_WrongNumArgs(interp, 1, objv, "className");
+ return TCL_ERROR;
+ }
+ oPtr = (Object *) Tcl_GetObjectFromObj(interp, objv[1]);
+ if (oPtr == NULL) {
+ return TCL_ERROR;
+ }
+ if (oPtr->classPtr == NULL) {
+ Tcl_AppendResult(interp, "\"", TclGetString(objv[1]),
+ "\" is not a class", NULL);
+ return TCL_ERROR;
+ }
+ clsPtr = oPtr->classPtr;
+
+ FOREACH(variableObj, clsPtr->variables) {
+ Tcl_ListObjAppendElement(NULL, Tcl_GetObjResult(interp), variableObj);
+ }
+ return TCL_OK;
+}
+
+/*
* Local Variables:
* mode: c
* c-basic-offset: 4
Index: generic/tclOOInt.h
===================================================================
RCS file: /cvsroot/tcl/oocore/generic/tclOOInt.h,v
retrieving revision 1.34
diff -w -u -r1.34 tclOOInt.h
--- generic/tclOOInt.h 2 Jun 2008 13:59:58 -0000 1.34
+++ generic/tclOOInt.h 28 Jun 2008 23:23:26 -0000
@@ -175,6 +175,7 @@
Tcl_ObjectMapMethodNameProc mapMethodNameProc;
/* Function to allow remapping of method
* names. For itcl-ng. */
+ LIST_STATIC(Tcl_Obj *) variables;
} Object;
#define OBJECT_DELETED 1 /* Flag to say that an object has been
@@ -248,6 +249,7 @@
* object doesn't override with its own mixins
* (and filters and method implementations for
* when getting method chains). */
+ LIST_STATIC(Tcl_Obj *) variables;
} Class;
/*
@@ -420,6 +422,9 @@
MODULE_SCOPE int TclOODefineUnexportObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const *objv);
+MODULE_SCOPE int TclOODefineVariablesObjCmd(ClientData clientData,
+ Tcl_Interp *interp, int objc,
+ Tcl_Obj *const *objv);
MODULE_SCOPE int TclOODefineClassObjCmd(ClientData clientData,
Tcl_Interp *interp, int objc,
Tcl_Obj *const *objv);
@@ -485,6 +490,7 @@
MODULE_SCOPE Foundation *TclOOGetFoundation(Tcl_Interp *interp);
MODULE_SCOPE Tcl_Obj * TclOOGetFwdFromMethod(Method *mPtr);
MODULE_SCOPE Proc * TclOOGetProcFromMethod(Method *mPtr);
+MODULE_SCOPE Tcl_Obj * TclOOGetMethodBody(Method *mPtr);
MODULE_SCOPE int TclOOGetSortedClassMethodList(Class *clsPtr,
int flags, const char ***stringsPtr);
MODULE_SCOPE int TclOOGetSortedMethodList(Object *oPtr, int flags,
@@ -503,6 +509,7 @@
Class *superPtr);
MODULE_SCOPE void TclOOStashContext(Tcl_Obj *objPtr,
CallContext *contextPtr);
+MODULE_SCOPE void TclOOSetupVariableResolver(Tcl_Namespace *nsPtr);
/*
* Include all the private API, generated from tclOO.decls.
Index: generic/tclOOMethod.c
===================================================================
RCS file: /cvsroot/tcl/oocore/generic/tclOOMethod.c,v
retrieving revision 1.25
diff -w -u -r1.25 tclOOMethod.c
--- generic/tclOOMethod.c 1 Jun 2008 08:05:46 -0000 1.25
+++ generic/tclOOMethod.c 28 Jun 2008 23:23:28 -0000
@@ -17,6 +17,12 @@
#include "tclInt.h"
#include "tclOOInt.h"
+#if 0
+#define DBPRINT(format, ...) (fprintf(stderr, "DEBUG:" format "\n", __VA_ARGS__))
+#else
+#define DBPRINT(format, ...) ((void) 0)
+#endif
+
/*
* Structure used to help delay computing names of objects or classes for
* [info frame] until needed, making invokation faster in the normal case.
@@ -45,6 +51,20 @@
} PMFrameData;
/*
+ * Structure used to pass information about variable resolution to the
+ * on-the-ground resolvers used when working with resolved compiled variables.
+ */
+
+typedef struct {
+ Tcl_ResolvedVarInfo info; /* "Type" information so that the compiled
+ * variable can be linked to the namespace
+ * variable at the right time. */
+ Tcl_Obj *variableObj; /* The name of the variable. */
+ Tcl_Var cachedObjectVar; /* TODO: When to flush this cache? Can class
+ * variables be cached? */
+} OOResVarInfo;
+
+/*
* Function declarations for things defined in this file.
*/
@@ -76,6 +96,13 @@
static void DeleteForwardMethod(ClientData clientData);
static int CloneForwardMethod(Tcl_Interp *interp,
ClientData clientData, ClientData *newClientData);
+static int ProcedureMethodVarResolver(Tcl_Interp *interp,
+ const char *varName, Tcl_Namespace *contextNs,
+ int flags, Tcl_Var *varPtr);
+static int ProcedureMethodCompiledVarResolver(Tcl_Interp *interp,
+ const char *varName, int length,
+ Tcl_Namespace *contextNs,
+ Tcl_ResolvedVarInfo **rPtrPtr);
/*
* The types of methods defined by the core OO system.
@@ -91,6 +118,15 @@
};
/*
+ * Helper macros (derived from things private to tclVar.c)
+ */
+
+#define TclVarTable(contextNs) \
+ ((Tcl_HashTable *) (&((Namespace *) (contextNs))->varTable))
+#define TclVarHashGetValue(hPtr) \
+ ((Tcl_Var) ((char *)hPtr - TclOffset(VarInHash, entry)))
+
+/*
* ----------------------------------------------------------------------
*
* Tcl_NewInstanceMethod --
@@ -314,6 +350,7 @@
pmPtr->version = TCLOO_PROCEDURE_METHOD_VERSION;
pmPtr->flags = flags & USE_DECLARER_NS;
pmPtr->refCount = 1;
+
method = TclOOMakeProcInstanceMethod(interp, oPtr, flags, nameObj,
argsObj, bodyObj, &procMethodType, pmPtr, &pmPtr->procPtr);
if (method == NULL) {
@@ -375,9 +412,8 @@
pmPtr->flags = flags & USE_DECLARER_NS;
pmPtr->refCount = 1;
- method = TclOOMakeProcMethod(interp, clsPtr, flags, nameObj,
- procName, argsObj, bodyObj, &procMethodType, pmPtr,
- &pmPtr->procPtr);
+ method = TclOOMakeProcMethod(interp, clsPtr, flags, nameObj, procName,
+ argsObj, bodyObj, &procMethodType, pmPtr, &pmPtr->procPtr);
if (argsLen == -1) {
Tcl_DecrRefCount(argsObj);
@@ -833,6 +869,172 @@
/*
* ----------------------------------------------------------------------
*
+ * TclOOSetupVariableResolver, etc. --
+ *
+ * Variable resolution engine used to connect declared variables to local
+ * variables used in methods. The compiled variable resolver is more
+ * important, but both are needed as it is possible to have a variable
+ * that is only referred to in ways that aren't compilable and we can't
+ * force LVT presence. [TIP #320]
+ *
+ * ----------------------------------------------------------------------
+ */
+
+void
+TclOOSetupVariableResolver(
+ Tcl_Namespace *nsPtr)
+{
+ Tcl_ResolverInfo info;
+
+ Tcl_GetNamespaceResolvers(nsPtr, &info);
+ if (info.compiledVarResProc == NULL) {
+ Tcl_SetNamespaceResolvers(nsPtr, NULL, ProcedureMethodVarResolver,
+ ProcedureMethodCompiledVarResolver);
+ }
+}
+
+static int
+ProcedureMethodVarResolver(
+ Tcl_Interp *interp,
+ const char *varName,
+ Tcl_Namespace *contextNs,
+ int flags,
+ Tcl_Var *varPtr)
+{
+ Interp *iPtr = (Interp *) interp;
+ CallFrame *framePtr = iPtr->varFramePtr;
+ CallContext *contextPtr;
+ Tcl_Obj *variableObj;
+ Tcl_HashEntry *hPtr;
+ int i, isNew;
+
+ if (framePtr == NULL || !(framePtr->isProcCallFrame & FRAME_IS_METHOD)) {
+ return TCL_CONTINUE;
+ }
+ contextPtr = framePtr->clientData;
+
+ if (contextPtr->callPtr->chain[contextPtr->index]
+ .mPtr->declaringClassPtr != NULL) {
+ FOREACH(variableObj, contextPtr->callPtr->chain[contextPtr->index]
+ .mPtr->declaringClassPtr->variables) {
+ if (!strcmp(Tcl_GetString(variableObj), varName)) {
+ goto gotMatch;
+ }
+ }
+ return TCL_CONTINUE;
+ } else {
+ FOREACH(variableObj, contextPtr->oPtr->variables) {
+ if (!strcmp(Tcl_GetString(variableObj), varName)) {
+ goto gotMatch;
+ }
+ }
+ return TCL_CONTINUE;
+ }
+
+ gotMatch:
+ hPtr = Tcl_CreateHashEntry(TclVarTable(contextNs), (char *) variableObj,
+ &isNew);
+ if (isNew) {
+ TclSetVarNamespaceVar((Var *) TclVarHashGetValue(hPtr));
+ }
+ *varPtr = TclVarHashGetValue(hPtr);
+ return TCL_OK;
+}
+
+static Tcl_Var
+PMCVConnect(
+ Tcl_Interp *interp,
+ Tcl_ResolvedVarInfo *rPtr)
+{
+ OOResVarInfo *infoPtr = (OOResVarInfo *) rPtr;
+ Interp *iPtr = (Interp *) interp;
+ CallFrame *framePtr = iPtr->varFramePtr;
+ CallContext *contextPtr;
+ Tcl_Obj *variableObj;
+ Tcl_HashEntry *hPtr;
+ int i, isNew, cacheIt;
+ const char *varName = Tcl_GetString(infoPtr->variableObj);
+
+ if (framePtr == NULL || !(framePtr->isProcCallFrame & FRAME_IS_METHOD)) {
+ return NULL;
+ }
+ contextPtr = framePtr->clientData;
+
+ if (infoPtr->cachedObjectVar) {
+ return infoPtr->cachedObjectVar;
+ }
+
+ if (contextPtr->callPtr->chain[contextPtr->index]
+ .mPtr->declaringClassPtr != NULL) {
+ FOREACH(variableObj, contextPtr->callPtr->chain[contextPtr->index]
+ .mPtr->declaringClassPtr->variables) {
+ if (!strcmp(Tcl_GetString(variableObj), varName)) {
+ cacheIt = 0;
+ goto gotMatch;
+ }
+ }
+ } else {
+ FOREACH(variableObj, contextPtr->oPtr->variables) {
+ if (!strcmp(Tcl_GetString(variableObj), varName)) {
+ cacheIt = 1;
+ goto gotMatch;
+ }
+ }
+ }
+ return NULL;
+
+ gotMatch:
+ hPtr = Tcl_CreateHashEntry(TclVarTable(contextPtr->oPtr->namespacePtr),
+ (char *) variableObj, &isNew);
+ if (isNew) {
+ TclSetVarNamespaceVar((Var *) TclVarHashGetValue(hPtr));
+ }
+ if (cacheIt) {
+ infoPtr->cachedObjectVar = TclVarHashGetValue(hPtr);
+ }
+ return TclVarHashGetValue(hPtr);
+}
+
+static void
+PMCVDelete(
+ Tcl_ResolvedVarInfo *rPtr)
+{
+ OOResVarInfo *infoPtr = (OOResVarInfo *) rPtr;
+
+ Tcl_DecrRefCount(infoPtr->variableObj);
+ ckfree((char *) infoPtr);
+}
+
+static int
+ProcedureMethodCompiledVarResolver(
+ Tcl_Interp *interp,
+ const char *varName,
+ int length,
+ Tcl_Namespace *contextNs,
+ Tcl_ResolvedVarInfo **rPtrPtr)
+{
+ OOResVarInfo *infoPtr;
+ Tcl_Obj *variableObj = Tcl_NewStringObj(varName, length);
+
+ if (strstr(Tcl_GetString(variableObj), "::") != NULL ||
+ Tcl_StringMatch(Tcl_GetString(variableObj), "*(*)")) {
+ Tcl_DecrRefCount(variableObj);
+ return TCL_CONTINUE;
+ }
+
+ infoPtr = (OOResVarInfo *) ckalloc(sizeof(OOResVarInfo));
+ infoPtr->info.fetchProc = PMCVConnect;
+ infoPtr->info.deleteProc = PMCVDelete;
+ infoPtr->cachedObjectVar = NULL;
+ infoPtr->variableObj = variableObj;
+ Tcl_IncrRefCount(variableObj);
+ *rPtrPtr = &infoPtr->info;
+ return TCL_OK;
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
* RenderDeclarerName --
*
* Returns the name of the entity (object or class) which declared a
@@ -1200,6 +1402,21 @@
}
Tcl_Obj *
+TclOOGetMethodBody(
+ Method *mPtr)
+{
+ if (mPtr->typePtr == &procMethodType) {
+ ProcedureMethod *pmPtr = mPtr->clientData;
+
+ if (pmPtr->procPtr->bodyPtr->bytes == NULL) {
+ (void) Tcl_GetString(pmPtr->procPtr->bodyPtr);
+ }
+ return pmPtr->procPtr->bodyPtr;
+ }
+ return NULL;
+}
+
+Tcl_Obj *
TclOOGetFwdFromMethod(
Method *mPtr)
{
Index: tests/oo.test
===================================================================
RCS file: /cvsroot/tcl/oocore/tests/oo.test,v
retrieving revision 1.32
diff -w -u -r1.32 oo.test
--- tests/oo.test 26 Jun 2008 15:10:50 -0000 1.32
+++ tests/oo.test 28 Jun 2008 23:23:30 -0000
@@ -1123,7 +1123,7 @@
} -returnCodes 1 -result {NOTANOBJECT does not refer to an object}
test oo-16.3 {OO: object introspection} -body {
info object gorp oo::object
-} -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be class, definition, filters, forward, isa, methods, mixins, or vars}
+} -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be class, definition, filters, forward, isa, methods, mixins, variables, or vars}
test oo-16.4 {OO: object introspection} -setup {
oo::class create meta { superclass oo::class }
[meta create instance1] create instance2
@@ -1228,7 +1228,7 @@
} -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 constructor, definition, destructor, filters, forward, instances, methods, mixins, subclasses, or superclasses}
+} -returnCodes 1 -result {unknown or ambiguous subcommand "gorp": must be constructor, definition, destructor, filters, forward, instances, methods, mixins, subclasses, superclasses, or variables}
test oo-17.5 {OO: class introspection} -setup {
oo::class create testClass
} -body {
@@ -1787,6 +1787,193 @@
lappend result [foo $m1] [bar $m2]
} -result {ok ok ok ok ok ok good ok}
+test oo-26.1 {variables declaration - class introspection} -setup {
+ oo::class create foo
+} -cleanup {
+ foo destroy
+} -body {
+ oo::define foo variable a b c
+ info class variables foo
+} -result {a b c}
+test oo-26.2 {variables declaration - object introspection} -setup {
+ oo::object create foo
+} -cleanup {
+ foo destroy
+} -body {
+ oo::objdefine foo variable a b c
+ info object variables foo
+} -result {a b c}
+test oo-26.3 {variables declaration - basic behaviour} -setup {
+ oo::class create master
+} -cleanup {
+ master destroy
+} -body {
+ oo::class create foo {
+ superclass master
+ variable x!
+ constructor {} {set x! 1}
+ method y {} {incr x!}
+ }
+ foo create bar
+ bar y
+ bar y
+} -result 3
+test oo-26.4 {variables declaration - destructors too} -setup {
+ oo::class create master
+ set result bad!
+} -cleanup {
+ master destroy
+} -body {
+ oo::class create foo {
+ superclass master
+ variable x!
+ constructor {} {set x! 1}
+ method y {} {incr x!}
+ destructor {set ::result ${x!}}
+ }
+ foo create bar
+ bar y
+ bar y
+ bar destroy
+ return $result
+} -result 3
+test oo-26.5 {variables declaration - object-bound variables} -setup {
+ oo::object create foo
+} -cleanup {
+ foo destroy
+} -body {
+ oo::objdefine foo {
+ variable x!
+ method y {} {incr x!}
+ }
+ foo y
+ foo y
+} -result 2
+test oo-26.6 {variables declaration - non-interference of levels} -setup {
+ oo::class create master
+} -cleanup {
+ master destroy
+} -body {
+ oo::class create foo {
+ superclass master
+ variable x!
+ constructor {} {set x! 1}
+ method y {} {incr x!}
+ }
+ foo create bar
+ oo::objdefine bar {
+ variable y!
+ method y {} {list [next] [incr y!] [info var] [info local]}
+ export eval
+ }
+ bar y
+ list [bar y] [lsort [info object vars bar]] [bar eval {info vars *!}]
+} -result {{3 2 y! {}} {x! y!} {x! y!}}
+test oo-26.7 {variables declaration - one underlying variable space} -setup {
+ oo::class create master
+} -cleanup {
+ master destroy
+} -body {
+ oo::class create foo {
+ superclass master
+ variable x!
+ constructor {} {set x! 1}
+ method y {} {incr x!}
+ }
+ oo::class create foo2 {
+ superclass foo
+ variable y!
+ constructor {} {set y! 42; next}
+ method x {} {incr y! -1}
+ }
+ foo2 create bar
+ oo::objdefine bar {
+ variable x! y!
+ method z {} {list ${x!} ${y!}}
+ }
+ bar y
+ bar x
+ list [bar y] [bar x] [bar z]
+} -result {3 40 {3 40}}
+test oo-26.8 {variables declaration - error cases - ns separators} -body {
+ oo::define oo::object variable bad::var
+} -returnCodes error -result {invalid declared variable name "bad::var": must not contain namespace separators}
+test oo-26.9 {variables declaration - error cases - arrays} -body {
+ oo::define oo::object variable bad(var)
+} -returnCodes error -result {invalid declared variable name "bad(var)": must not refer to an array element}
+test oo-26.10 {variables declaration - no instance var leaks with class resolvers} -setup {
+ oo::class create master
+} -cleanup {
+ master destroy
+} -body {
+ oo::class create foo {
+ superclass master
+ variable clsvar
+ constructor {} {
+ set clsvar 0
+ }
+ method step {} {
+ incr clsvar
+ return
+ }
+ method value {} {
+ return $clsvar
+ }
+ }
+ foo create inst1
+ inst1 step
+ foo create inst2
+ inst2 step
+ inst1 step
+ inst2 step
+ inst1 step
+ list [inst1 value] [inst2 value]
+} -result {3 2}
+test oo-26.11 {variables declaration - no instance var leaks with class resolvers} -setup {
+ oo::class create master
+} -cleanup {
+ master destroy
+} -body {
+ oo::class create foo {
+ superclass master
+ variable clsvar
+ constructor {} {
+ set clsvar 0
+ }
+ method step {} {
+ incr clsvar
+ return
+ }
+ method value {} {
+ return $clsvar
+ }
+ }
+ foo create inst1
+ oo::objdefine inst1 {
+ variable clsvar
+ method reinit {} {
+ set clsvar 0
+ }
+ }
+ foo create inst2
+ oo::objdefine inst2 {
+ variable clsvar
+ method reinit {} {
+ set clsvar 0
+ }
+ }
+ inst1 step
+ inst2 step
+ inst1 reinit
+ inst2 reinit
+ inst1 step
+ inst2 step
+ inst1 step
+ inst2 step
+ inst1 step
+ list [inst1 value] [inst2 value]
+} -result {3 2}
+
cleanupTests
return