Tcl Source Code

Artifact [c7d3a3a4bc]
Login

Artifact c7d3a3a4bc9a7acd2a23880732435e8922316a82:

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