[Mono-list] Properties are now working.

Miguel de Icaza miguel@ximian.com
Sun, 14 Oct 2001 18:57:05 -0400


Hey guys,

   Today I got properties working in the compiler (get/set, error
checking, instance or class support).  Very simple changes to the work
from friday.  I will hold on a bit on indexers, as that requires to
tackle also arrays at the same time.

   I am now going to try to fix the compiler so we can handle test-22
which exhibits an interesting bug. 

Miguel.

Index: ChangeLog
===================================================================
RCS file: /cvs/public/mcs/mcs/ChangeLog,v
retrieving revision 1.132
diff -u -r1.132 ChangeLog
--- ChangeLog	2001/10/12 18:52:18	1.132
+++ ChangeLog	2001/10/14 19:02:26
@@ -1,3 +1,13 @@
+2001-10-14  Miguel de Icaza  <miguel@ximian.com>
+
+	* expression.cs (PropertyExpr): Make this into an
+	ExpressionStatement, and support the EmitStatement code path. 
+
+	Perform get/set error checking, clean up the interface.
+
+	* assign.cs: recognize PropertyExprs as targets, and if so, turn
+	them into toplevel access objects.
+
 2001-10-12  Miguel de Icaza  <miguel@ximian.com>
 
 	* expression.cs: PropertyExpr::PropertyExpr: use work around the
Index: assign.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/assign.cs,v
retrieving revision 1.16
diff -u -r1.16 assign.cs
--- assign.cs	2001/10/12 14:18:21	1.16
+++ assign.cs	2001/10/14 19:02:26
@@ -50,6 +50,19 @@
 			if (target == null || source == null)
 				return null;
 
+			//
+			// If we are doing a property assignment, then
+			// set the `value' field on the property, and Resolve
+			// it.
+			//
+			if (target is PropertyExpr){
+				PropertyExpr property_assign = (PropertyExpr) target;
+				
+				property_assign.Value = source;
+				
+				return property_assign.Resolve (ec);
+			}
+			
 			Type target_type = target.Type;
 			
 			Type source_type = source.Type;
Index: expression.cs
===================================================================
RCS file: /cvs/public/mcs/mcs/expression.cs,v
retrieving revision 1.82
diff -u -r1.82 expression.cs
--- expression.cs	2001/10/12 18:52:18	1.82
+++ expression.cs	2001/10/14 19:02:38
@@ -3899,28 +3899,25 @@
 				a.Emit (ec);
 			}
 		}
-		
-		public override void Emit (EmitContext ec)
+
+		public static void EmitCall (EmitContext ec,
+					     bool is_static, Expression instance_expr,
+					     MethodBase method, ArrayList Arguments)
 		{
-			bool is_static = method.IsStatic;
 			ILGenerator ig = ec.ig;
 			bool struct_call = false;
 				
 			if (!is_static){
-				MethodGroupExpr mg = (MethodGroupExpr) this.expr;
-
 				//
 				// If this is ourselves, push "this"
 				//
-				if (mg.InstanceExpression == null){
+				if (instance_expr == null){
 					ig.Emit (OpCodes.Ldarg_0);
 				} else {
-					Expression ie = mg.InstanceExpression;
-					
 					//
 					// Push the instance expression
 					//
-					if (ie.Type.IsSubclassOf (TypeManager.value_type)){
+					if (instance_expr.Type.IsSubclassOf (TypeManager.value_type)){
 
 						struct_call = true;
 
@@ -3931,16 +3928,18 @@
 						//
 						// If not we have to use some temporary storage for
 						// it.
-						if (ie is MemoryLocation)
-							((MemoryLocation) ie).AddressOf (ec);
+						if (instance_expr is MemoryLocation)
+							((MemoryLocation) instance_expr).AddressOf (ec);
 						else {
-							ie.Emit (ec);
-							LocalBuilder temp = ec.GetTemporaryStorage (ie.Type);
+							Type t = instance_expr.Type;
+							
+							instance_expr.Emit (ec);
+							LocalBuilder temp = ec.GetTemporaryStorage (t);
 							ig.Emit (OpCodes.Stloc, temp);
 							ig.Emit (OpCodes.Ldloca, temp);
 						}
 					} else 
-						ie.Emit (ec);
+						instance_expr.Emit (ec);
 				}
 			}
 
@@ -3959,6 +3958,13 @@
 					ig.Emit (OpCodes.Callvirt, (ConstructorInfo) method);
 			}
 		}
+		
+		public override void Emit (EmitContext ec)
+		{
+			MethodGroupExpr mg = (MethodGroupExpr) this.expr;
+			
+			EmitCall (ec, method.IsStatic, mg.InstanceExpression, method, Arguments);
+		}
 
 		public override void EmitStatement (EmitContext ec)
 		{
@@ -4505,34 +4511,58 @@
 	}
 	
 	// <summary>
-	//   Fully resolved expression that evaluates to a Property
+	//   Expression that evaluates to a Property.  The Assign class
+	//   might set the `Value' expression if we are in an assignment. 
 	// </summary>
-	public class PropertyExpr : Expression, LValue {
+	public class PropertyExpr : ExpressionStatement {
 		public readonly PropertyInfo PropertyInfo;
 		public readonly bool IsStatic;
 		MethodInfo [] Accessors;
 		Location loc;
 		
 		Expression instance_expr;
+		Expression value;
 		
-		public PropertyExpr (PropertyInfo pi, Location loc)
+		public PropertyExpr (PropertyInfo pi, Location l)
 		{
 			PropertyInfo = pi;
 			eclass = ExprClass.PropertyAccess;
 			IsStatic = false;
-
+			loc = l;
 			Accessors = TypeManager.GetAccessors (pi);
 
 			if (Accessors != null)
-				for (int i = 0; i < Accessors.Length; i++)
-					if (Accessors [i].IsStatic)
-						IsStatic = true;
+				for (int i = 0; i < Accessors.Length; i++){
+					if (Accessors [i] != null)
+						if (Accessors [i].IsStatic)
+							IsStatic = true;
+				}
 			else
 				Accessors = new MethodInfo [2];
 			
 			type = pi.PropertyType;
 		}
 
+		//
+		// Controls the Value of the PropertyExpr.  If the value
+		// is null, then the property is being used in a `read' mode.
+		// otherwise the property is used in assignment mode.
+		//
+		// The value is set to a fully resolved type by assign.
+		//
+		public Expression Value {
+			get {
+				return value;
+			}
+
+			set {
+				this.value = value;
+			}
+		}
+
+		//
+		// The instance expression associated with this expression
+		//
 		public Expression InstanceExpression {
 			set {
 				instance_expr = value;
@@ -4544,44 +4574,46 @@
 		}
 		
 		override public Expression DoResolve (EmitContext ec)
-		{
-			// We are born in resolved state. 
-			return this;
-		}
-
-		public Expression LValueResolve (EmitContext ec)
 		{
-			if (!PropertyInfo.CanWrite){
-				Report.Error (200, loc, 
-					      "The property `" + PropertyInfo.Name +
-					      "' can not be assigned to, as it has not set accessor");
-				return null;
+			if (value == null){
+				if (!PropertyInfo.CanRead){
+					Report.Error (154, loc, 
+						      "The property `" + PropertyInfo.Name +
+						      "' can not be used in " +
+						      "this context because it lacks a get accessor");
+					return null;
+				}
+			} else {
+				if (!PropertyInfo.CanWrite){
+					Report.Error (200, loc, 
+						      "The property `" + PropertyInfo.Name +
+						      "' can not be assigned to, as it has not set accessor");
+					return null;
+				}
 			}
 			
 			return this;
-		}
-		
-		public void Store (EmitContext ec)
-		{
 		}
-		
+
 		override public void Emit (EmitContext ec)
 		{
-			//
-			// This really should be done in Resolve, but
-			// at that point we might be used as an LValue
-			// and it is valid to only have a setter and no getter.
-			//
+			if (value == null)
+				Invocation.EmitCall (ec, IsStatic, instance_expr, Accessors [0], null);
+			else {
+				Argument arg = new Argument (value, Argument.AType.Expression);
+				ArrayList args = new ArrayList ();
 
-			if (!PropertyInfo.CanRead){
-				Report.Error (154, loc, 
-					      "The property `" + PropertyInfo.Name +
-					      "' can not be used in " +
-					      "this context because it lacks a get accessor");
-				return;
+				args.Add (arg);
+				Invocation.EmitCall (ec, IsStatic, instance_expr, Accessors [1], args);
 			}
-			
-			throw new Exception ("Unimplemented");
+		}
+
+		override public void EmitStatement (EmitContext ec)
+		{
+			Emit (ec);
+			if (value == null){
+				ec.ig.Emit (OpCodes.Pop);
+			}
 		}
 	}