monotouch uses the real HttpWebRequest
[mcs.git] / mcs / assign.cs
blob8be80e55a9fec7178c889b2a2162124eebdc2410
1 //
2 // assign.cs: Assignments.
3 //
4 // Author:
5 // Miguel de Icaza (miguel@ximian.com)
6 // Martin Baulig (martin@ximian.com)
7 // Marek Safar (marek.safar@gmail.com)
8 //
9 // Dual licensed under the terms of the MIT X11 or GNU GPL
11 // Copyright 2001, 2002, 2003 Ximian, Inc.
12 // Copyright 2004-2008 Novell, Inc
14 using System;
15 using System.Reflection;
16 using System.Reflection.Emit;
17 using System.Collections;
19 namespace Mono.CSharp {
21 /// <summary>
22 /// This interface is implemented by expressions that can be assigned to.
23 /// </summary>
24 /// <remarks>
25 /// This interface is implemented by Expressions whose values can not
26 /// store the result on the top of the stack.
27 ///
28 /// Expressions implementing this (Properties, Indexers and Arrays) would
29 /// perform an assignment of the Expression "source" into its final
30 /// location.
31 ///
32 /// No values on the top of the stack are expected to be left by
33 /// invoking this method.
34 /// </remarks>
35 public interface IAssignMethod {
37 // This is an extra version of Emit. If leave_copy is `true'
38 // A copy of the expression will be left on the stack at the
39 // end of the code generated for EmitAssign
41 void Emit (EmitContext ec, bool leave_copy);
44 // This method does the assignment
45 // `source' will be stored into the location specified by `this'
46 // if `leave_copy' is true, a copy of `source' will be left on the stack
47 // if `prepare_for_load' is true, when `source' is emitted, there will
48 // be data on the stack that it can use to compuatate its value. This is
49 // for expressions like a [f ()] ++, where you can't call `f ()' twice.
51 void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load);
54 For simple assignments, this interface is very simple, EmitAssign is called with source
55 as the source expression and leave_copy and prepare_for_load false.
57 For compound assignments it gets complicated.
59 EmitAssign will be called as before, however, prepare_for_load will be
60 true. The @source expression will contain an expression
61 which calls Emit. So, the calls look like:
63 this.EmitAssign (ec, source, false, true) ->
64 source.Emit (ec); ->
65 [...] ->
66 this.Emit (ec, false); ->
67 end this.Emit (ec, false); ->
68 end [...]
69 end source.Emit (ec);
70 end this.EmitAssign (ec, source, false, true)
73 When prepare_for_load is true, EmitAssign emits a `token' on the stack that
74 Emit will use for its state.
76 Let's take FieldExpr as an example. assume we are emitting f ().y += 1;
78 Here is the call tree again. This time, each call is annotated with the IL
79 it produces:
81 this.EmitAssign (ec, source, false, true)
82 call f
83 dup
85 Binary.Emit ()
86 this.Emit (ec, false);
87 ldfld y
88 end this.Emit (ec, false);
90 IntConstant.Emit ()
91 ldc.i4.1
92 end IntConstant.Emit
94 add
95 end Binary.Emit ()
97 stfld
98 end this.EmitAssign (ec, source, false, true)
100 Observe two things:
101 1) EmitAssign left a token on the stack. It was the result of f ().
102 2) This token was used by Emit
104 leave_copy (in both EmitAssign and Emit) tells the compiler to leave a copy
105 of the expression at that point in evaluation. This is used for pre/post inc/dec
106 and for a = x += y. Let's do the above example with leave_copy true in EmitAssign
108 this.EmitAssign (ec, source, true, true)
109 call f
112 Binary.Emit ()
113 this.Emit (ec, false);
114 ldfld y
115 end this.Emit (ec, false);
117 IntConstant.Emit ()
118 ldc.i4.1
119 end IntConstant.Emit
122 end Binary.Emit ()
125 stloc temp
126 stfld
127 ldloc temp
128 end this.EmitAssign (ec, source, true, true)
130 And with it true in Emit
132 this.EmitAssign (ec, source, false, true)
133 call f
136 Binary.Emit ()
137 this.Emit (ec, true);
138 ldfld y
140 stloc temp
141 end this.Emit (ec, true);
143 IntConstant.Emit ()
144 ldc.i4.1
145 end IntConstant.Emit
148 end Binary.Emit ()
150 stfld
151 ldloc temp
152 end this.EmitAssign (ec, source, false, true)
154 Note that these two examples are what happens for ++x and x++, respectively.
158 /// <summary>
159 /// An Expression to hold a temporary value.
160 /// </summary>
161 /// <remarks>
162 /// The LocalTemporary class is used to hold temporary values of a given
163 /// type to "simulate" the expression semantics on property and indexer
164 /// access whose return values are void.
166 /// The local temporary is used to alter the normal flow of code generation
167 /// basically it creates a local variable, and its emit instruction generates
168 /// code to access this value, return its address or save its value.
170 /// If `is_address' is true, then the value that we store is the address to the
171 /// real value, and not the value itself.
173 /// This is needed for a value type, because otherwise you just end up making a
174 /// copy of the value on the stack and modifying it. You really need a pointer
175 /// to the origional value so that you can modify it in that location. This
176 /// Does not happen with a class because a class is a pointer -- so you always
177 /// get the indirection.
179 /// The `is_address' stuff is really just a hack. We need to come up with a better
180 /// way to handle it.
181 /// </remarks>
182 public class LocalTemporary : Expression, IMemoryLocation, IAssignMethod {
183 LocalBuilder builder;
184 bool is_address;
186 public LocalTemporary (Type t) : this (t, false) {}
188 public LocalTemporary (Type t, bool is_address)
190 type = t;
191 eclass = ExprClass.Value;
192 this.is_address = is_address;
195 public LocalTemporary (LocalBuilder b, Type t)
197 type = t;
198 eclass = ExprClass.Value;
199 loc = Location.Null;
200 builder = b;
203 public void Release (EmitContext ec)
205 ec.FreeTemporaryLocal (builder, type);
206 builder = null;
209 public override Expression CreateExpressionTree (EmitContext ec)
211 Arguments args = new Arguments (1);
212 args.Add (new Argument (this));
213 return CreateExpressionFactoryCall ("Constant", args);
216 public override Expression DoResolve (EmitContext ec)
218 return this;
221 public override Expression DoResolveLValue (EmitContext ec, Expression right_side)
223 return this;
226 public override void Emit (EmitContext ec)
228 ILGenerator ig = ec.ig;
230 if (builder == null)
231 throw new InternalErrorException ("Emit without Store, or after Release");
233 ig.Emit (OpCodes.Ldloc, builder);
234 // we need to copy from the pointer
235 if (is_address)
236 LoadFromPtr (ig, type);
239 #region IAssignMethod Members
241 public void Emit (EmitContext ec, bool leave_copy)
243 Emit (ec);
245 if (leave_copy)
246 Emit (ec);
249 public void EmitAssign (EmitContext ec, Expression source, bool leave_copy, bool prepare_for_load)
251 if (prepare_for_load)
252 throw new NotImplementedException ();
254 source.Emit (ec);
256 Store (ec);
258 if (leave_copy)
259 Emit (ec);
262 #endregion
264 public LocalBuilder Builder {
265 get { return builder; }
268 // NB: if you have `is_address' on the stack there must
269 // be a managed pointer. Otherwise, it is the type from
270 // the ctor.
271 public void Store (EmitContext ec)
273 ILGenerator ig = ec.ig;
274 if (builder == null)
275 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
277 ig.Emit (OpCodes.Stloc, builder);
280 public void AddressOf (EmitContext ec, AddressOp mode)
282 if (builder == null)
283 builder = ec.GetTemporaryLocal (is_address ? TypeManager.GetReferenceType (type): type);
285 // if is_address, than this is just the address anyways,
286 // so we just return this.
287 ILGenerator ig = ec.ig;
289 if (is_address)
290 ig.Emit (OpCodes.Ldloc, builder);
291 else
292 ig.Emit (OpCodes.Ldloca, builder);
295 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
297 type = storey.MutateType (type);
300 public bool PointsToAddress {
301 get {
302 return is_address;
307 /// <summary>
308 /// The Assign node takes care of assigning the value of source into
309 /// the expression represented by target.
310 /// </summary>
311 public class Assign : ExpressionStatement {
312 protected Expression target, source;
314 protected Assign (Expression target, Expression source, Location loc)
316 this.target = target;
317 this.source = source;
318 this.loc = loc;
321 public override Expression CreateExpressionTree (EmitContext ec)
323 Report.Error (832, loc, "An expression tree cannot contain an assignment operator");
324 return null;
327 public Expression Target {
328 get { return target; }
331 public Expression Source {
332 get { return source; }
335 public override Expression DoResolve (EmitContext ec)
337 bool ok = true;
338 source = source.Resolve (ec);
340 if (source == null) {
341 ok = false;
342 source = EmptyExpression.Null;
345 target = target.ResolveLValue (ec, source);
347 if (target == null || !ok)
348 return null;
350 Type target_type = target.Type;
351 Type source_type = source.Type;
353 eclass = ExprClass.Value;
354 type = target_type;
356 if (!(target is IAssignMethod)) {
357 Error_ValueAssignment (loc);
358 return null;
361 if ((source.eclass == ExprClass.Type) && (source is TypeExpr)) {
362 source.Error_UnexpectedKind (ec.DeclContainer, "variable or value", loc);
363 return null;
364 } else if ((RootContext.Version == LanguageVersion.ISO_1) &&
365 (source is MethodGroupExpr)){
366 ((MethodGroupExpr) source).ReportUsageError ();
367 return null;
370 if (!TypeManager.IsEqual (target_type, source_type)) {
371 if (TypeManager.IsDynamicType (source_type)) {
372 Arguments args = new Arguments (1);
373 args.Add (new Argument (source));
374 return new DynamicConversion (target_type, false, args, loc).Resolve (ec);
377 Expression resolved = ResolveConversions (ec);
379 if (resolved != this)
380 return resolved;
383 return this;
386 public override void MutateHoistedGenericType (AnonymousMethodStorey storey)
388 source.MutateHoistedGenericType (storey);
389 target.MutateHoistedGenericType (storey);
390 type = storey.MutateType (type);
393 protected virtual Expression ResolveConversions (EmitContext ec)
395 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
396 if (source == null)
397 return null;
399 return this;
402 void Emit (EmitContext ec, bool is_statement)
404 IAssignMethod t = (IAssignMethod) target;
405 t.EmitAssign (ec, source, !is_statement, this is CompoundAssign);
408 public override void Emit (EmitContext ec)
410 Emit (ec, false);
413 public override void EmitStatement (EmitContext ec)
415 Emit (ec, true);
418 protected override void CloneTo (CloneContext clonectx, Expression t)
420 Assign _target = (Assign) t;
422 _target.target = target.Clone (clonectx);
423 _target.source = source.Clone (clonectx);
427 class SimpleAssign : Assign {
428 public SimpleAssign (Expression target, Expression source)
429 : this (target, source, target.Location)
433 public SimpleAssign (Expression target, Expression source, Location loc)
434 : base (target, source, loc)
438 bool CheckEqualAssign (Expression t)
440 if (source is Assign) {
441 Assign a = (Assign) source;
442 if (t.Equals (a.Target))
443 return true;
444 return a is SimpleAssign && ((SimpleAssign) a).CheckEqualAssign (t);
446 return t.Equals (source);
449 public override Expression DoResolve (EmitContext ec)
451 Expression e = base.DoResolve (ec);
452 if (e == null || e != this)
453 return e;
455 if (CheckEqualAssign (target))
456 Report.Warning (1717, 3, loc, "Assignment made to same variable; did you mean to assign something else?");
458 return this;
462 // This class implements fields and events class initializers
463 public class FieldInitializer : Assign
466 // Keep resolved value because field initializers have their own rules
468 ExpressionStatement resolved;
469 IResolveContext rc;
471 public FieldInitializer (FieldBuilder field, Expression expression, IResolveContext rc)
472 : base (new FieldExpr (field, expression.Location), expression, expression.Location)
474 this.rc = rc;
475 if (!field.IsStatic)
476 ((FieldExpr)target).InstanceExpression = CompilerGeneratedThis.Instance;
479 public override Expression DoResolve (EmitContext ec)
481 // Field initializer can be resolved (fail) many times
482 if (source == null)
483 return null;
485 if (resolved == null) {
487 // Field initializers are tricky for partial classes. They have to
488 // share same costructor (block) but they have they own resolve scope.
491 // TODO: Use ResolveContext only
492 EmitContext f_ec = new EmitContext (rc, rc.DeclContainer, loc, null, TypeManager.void_type, 0, true);
493 f_ec.IsStatic = ec.IsStatic;
494 f_ec.CurrentBlock = ec.CurrentBlock;
496 EmitContext.Flags flags = EmitContext.Flags.InFieldInitializer;
497 if (ec.IsInUnsafeScope)
498 flags |= EmitContext.Flags.InUnsafe;
500 f_ec.Set (flags);
502 resolved = base.DoResolve (f_ec) as ExpressionStatement;
505 return resolved;
508 public override void EmitStatement (EmitContext ec)
510 if (resolved == null)
511 return;
513 if (resolved != this)
514 resolved.EmitStatement (ec);
515 else
516 base.EmitStatement (ec);
519 public bool IsComplexInitializer {
520 get { return !(source is Constant); }
523 public bool IsDefaultInitializer {
524 get {
525 Constant c = source as Constant;
526 if (c == null)
527 return false;
529 FieldExpr fe = (FieldExpr)target;
530 return c.IsDefaultInitializer (fe.Type);
535 class EventAddOrRemove : ExpressionStatement {
536 EventExpr target;
537 Binary.Operator op;
538 Expression source;
540 public EventAddOrRemove (Expression target, Binary.Operator op, Expression source, Location loc)
542 this.target = target as EventExpr;
543 this.op = op;
544 this.source = source;
545 this.loc = loc;
548 public override Expression CreateExpressionTree (EmitContext ec)
550 return new SimpleAssign (target, source).CreateExpressionTree (ec);
553 public override Expression DoResolve (EmitContext ec)
555 if (op != Binary.Operator.Addition && op != Binary.Operator.Subtraction)
556 target.Error_AssignmentEventOnly ();
558 source = source.Resolve (ec);
559 if (source == null)
560 return null;
562 source = Convert.ImplicitConversionRequired (ec, source, target.Type, loc);
563 if (source == null)
564 return null;
566 eclass = ExprClass.Value;
567 type = TypeManager.void_type;
568 return this;
571 public override void Emit (EmitContext ec)
573 if (RootContext.EvalMode)
574 EmitStatement (ec);
575 else
576 throw new InternalErrorException ("don't know what to emit");
579 public override void EmitStatement (EmitContext ec)
581 target.EmitAddOrRemove (ec, op == Binary.Operator.Addition, source);
586 // This class is used for compound assignments.
588 class CompoundAssign : Assign {
589 Binary.Operator op;
590 Expression original_source;
592 public CompoundAssign (Binary.Operator op, Expression target, Expression source)
593 : base (target, source, target.Location)
595 original_source = source;
596 this.op = op;
599 public sealed class TargetExpression : Expression {
600 Expression child;
601 public TargetExpression (Expression child)
603 this.child = child;
604 this.loc = child.Location;
607 public override Expression CreateExpressionTree (EmitContext ec)
609 throw new NotSupportedException ("ET");
612 public override Expression DoResolve (EmitContext ec)
614 child = child.Resolve (ec);
615 if (child == null)
616 return null;
617 type = child.Type;
618 eclass = ExprClass.Value;
619 return this;
622 public override void Emit (EmitContext ec)
624 child.Emit (ec);
628 public override Expression DoResolve (EmitContext ec)
630 original_source = original_source.Resolve (ec);
631 if (original_source == null)
632 return null;
634 MemberAccess ma = target as MemberAccess;
635 using (ec.Set (EmitContext.Flags.InCompoundAssignment)) {
636 target = target.Resolve (ec);
639 if (target == null)
640 return null;
642 if (target is MethodGroupExpr){
643 Error_CannotAssign (((MethodGroupExpr)target).Name, target.ExprClassName);
644 return null;
647 if (target is EventExpr)
648 return new EventAddOrRemove (target, op, original_source, loc).DoResolve (ec);
651 // Only now we can decouple the original source/target
652 // into a tree, to guarantee that we do not have side
653 // effects.
655 source = new Binary (op, new TargetExpression (target), original_source, true);
657 // TODO: TargetExpression breaks MemberAccess composition
658 if (target is DynamicMemberBinder) {
659 Arguments targs = ((DynamicMemberBinder) target).Arguments;
660 source = source.Resolve (ec);
662 Arguments args = new Arguments (2);
663 args.AddRange (targs);
664 args.Add (new Argument (source));
665 source = new DynamicMemberBinder (true, ma.Name, args, loc).Resolve (ec);
667 // Handles possible event addition/subtraction
668 if (op == Binary.Operator.Addition || op == Binary.Operator.Subtraction) {
669 args = new Arguments (2);
670 args.AddRange (targs);
671 args.Add (new Argument (original_source));
672 string method_prefix = op == Binary.Operator.Addition ?
673 Event.AEventAccessor.AddPrefix : Event.AEventAccessor.RemovePrefix;
675 Expression invoke = new DynamicInvocation (
676 new MemberAccess (original_source, method_prefix + ma.Name, loc), args, loc).Resolve (ec);
678 args = new Arguments (1);
679 args.AddRange (targs);
680 source = new DynamicEventCompoundAssign (ma.Name, args,
681 (ExpressionStatement) source, (ExpressionStatement) invoke, loc).Resolve (ec);
684 return source;
687 return base.DoResolve (ec);
690 protected override Expression ResolveConversions (EmitContext ec)
692 Type target_type = target.Type;
695 // 1. the return type is implicitly convertible to the type of target
697 if (Convert.ImplicitConversionExists (ec, source, target_type)) {
698 source = Convert.ImplicitConversion (ec, source, target_type, loc);
699 return this;
703 // Otherwise, if the selected operator is a predefined operator
705 Binary b = source as Binary;
706 if (b != null) {
708 // 2a. the operator is a shift operator
710 // 2b. the return type is explicitly convertible to the type of x, and
711 // y is implicitly convertible to the type of x
713 if ((b.Oper & Binary.Operator.ShiftMask) != 0 ||
714 Convert.ImplicitConversionExists (ec, original_source, target_type)) {
715 source = Convert.ExplicitConversion (ec, source, target_type, loc);
716 return this;
720 original_source.Error_ValueCannotBeConverted (ec, loc, target_type, false);
721 return null;
724 protected override void CloneTo (CloneContext clonectx, Expression t)
726 CompoundAssign ctarget = (CompoundAssign) t;
728 ctarget.original_source = ctarget.source = source.Clone (clonectx);
729 ctarget.target = target.Clone (clonectx);