BOO-988: Added else block to the for and while statements, and include a suite of...
[boo.git] / src / Boo.Lang.Compiler / Steps / ProcessAssignmentsToSpecialMembers.cs
blob20446bdb19d6c4be9bc83daf85c8fcbbd6da44b2
1 #region license
2 // Copyright (c) 2003, 2004, 2005 Rodrigo B. de Oliveira (rbo@acm.org)
3 // All rights reserved.
4 //
5 // Redistribution and use in source and binary forms, with or without modification,
6 // are permitted provided that the following conditions are met:
7 //
8 // * Redistributions of source code must retain the above copyright notice,
9 // this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright notice,
11 // this list of conditions and the following disclaimer in the documentation
12 // and/or other materials provided with the distribution.
13 // * Neither the name of Rodrigo B. de Oliveira nor the names of its
14 // contributors may be used to endorse or promote products derived from this
15 // software without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
18 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 // DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
21 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25 // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 // THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 #endregion
29 namespace Boo.Lang.Compiler.Steps
31 using Boo.Lang.Compiler.Ast;
32 using Boo.Lang.Compiler.TypeSystem;
34 /// <summary>
35 /// Summary description for ProcessAssignmentsToSpecialMembers.
36 /// </summary>
37 public abstract class ProcessAssignmentsToSpecialMembers : AbstractTransformerCompilerStep
39 Method _currentMethod;
41 override public void Run()
43 if (0 == Errors.Count)
45 Visit(CompileUnit);
49 override public void OnInterfaceDefinition(InterfaceDefinition node)
53 override public void OnEnumDefinition(EnumDefinition node)
57 override public void OnMethod(Method node)
59 _currentMethod = node;
60 Visit(node.Body);
63 override public void OnConstructor(Constructor node)
65 OnMethod(node);
68 override public void LeaveBinaryExpression(BinaryExpression node)
70 if (IsAssignmentToSpecialMember(node))
72 ProcessAssignmentToSpecialMember(node);
76 protected bool IsAssignmentToSpecialMember(BinaryExpression node)
78 if (BinaryOperatorType.Assign == node.Operator &&
79 NodeType.MemberReferenceExpression == node.Left.NodeType)
81 MemberReferenceExpression memberRef = node.Left as MemberReferenceExpression;
82 Expression target = memberRef.Target;
83 return !IsTerminalReferenceNode(target)
84 && IsSpecialMemberTarget(target);
86 return false;
89 protected abstract bool IsSpecialMemberTarget(Expression container);
91 public class ChainItem
93 public Expression Container;
94 public InternalLocal Local;
96 public ChainItem(Expression container)
98 this.Container = container;
102 void ProcessAssignmentToSpecialMember(BinaryExpression node)
104 MemberReferenceExpression memberRef = (MemberReferenceExpression)node.Left;
105 List chain = WalkMemberChain(memberRef);
106 if (null == chain || 0 == chain.Count) return;
108 MethodInvocationExpression eval = CodeBuilder.CreateEvalInvocation(node.LexicalInfo);
110 // right hand side should always be executed before
111 // left hand side
112 InternalLocal value = DeclareTempLocal(GetExpressionType(node.Right));
113 eval.Arguments.Add(
114 CodeBuilder.CreateAssignment(
115 CodeBuilder.CreateReference(value),
116 node.Right));
118 foreach (ChainItem item in chain)
120 item.Local = DeclareTempLocal(item.Container.ExpressionType);
121 BinaryExpression tempInitialization = CodeBuilder.CreateAssignment(
122 node.LexicalInfo,
123 CodeBuilder.CreateReference(item.Local),
124 item.Container.CloneNode());
125 item.Container.ParentNode.Replace(item.Container,
126 CodeBuilder.CreateReference(item.Local));
127 eval.Arguments.Add(tempInitialization);
130 eval.Arguments.Add(
131 CodeBuilder.CreateAssignment(node.LexicalInfo,
132 node.Left,
133 CodeBuilder.CreateReference(value)));
135 PropagateChanges(eval, chain);
137 if (NodeType.ExpressionStatement != node.ParentNode.NodeType)
139 eval.Arguments.Add(CodeBuilder.CreateReference(value));
140 BindExpressionType(eval, value.Type);
143 ReplaceCurrentNode(eval);
146 protected virtual void PropagateChanges(MethodInvocationExpression eval, List chain)
148 foreach (ChainItem item in chain.Reversed)
150 eval.Arguments.Add(
151 CodeBuilder.CreateAssignment(
152 item.Container.CloneNode(),
153 CodeBuilder.CreateReference(item.Local)));
157 protected virtual List WalkMemberChain(MemberReferenceExpression memberRef)
159 List chain = new List();
160 while (true)
162 MemberReferenceExpression container = memberRef.Target as MemberReferenceExpression;
163 if (null == container ||
164 (IsSpecialMemberTarget(container)
165 && IsReadOnlyMember(container)))
167 Warnings.Add(
168 CompilerWarningFactory.AssignmentToTemporary(memberRef));
169 return null;
171 if (IsSpecialMemberTarget(container)
172 && EntityType.Field != container.Entity.EntityType)
174 chain.Insert(0, new ChainItem(container));
176 if (IsTerminalReferenceNode(container.Target))
178 break;
180 memberRef = container;
182 return chain;
185 protected virtual bool IsTerminalReferenceNode(Expression target)
187 NodeType type = target.NodeType;
188 return
189 NodeType.ReferenceExpression == type ||
190 NodeType.SelfLiteralExpression == type ||
191 NodeType.SuperLiteralExpression == type ||
192 ProcessMethodBodies.IsArraySlicing(target) ||
193 !IsSpecialMemberTarget(target);
196 protected virtual bool IsReadOnlyMember(MemberReferenceExpression container)
198 switch (container.Entity.EntityType)
200 case EntityType.Property:
202 return ((IProperty)container.Entity).GetSetMethod() == null;
204 case EntityType.Field:
206 return TypeSystemServices.IsReadOnlyField((IField)container.Entity);
209 return true;
212 InternalLocal DeclareTempLocal(IType localType)
214 return CodeBuilder.DeclareTempLocal(_currentMethod, localType);