Merge pull request #778 from cmorris98/master
[mono.git] / mcs / class / dlr / Runtime / Microsoft.Dynamic / Interpreter / LocalVariables.cs
1 /* ****************************************************************************
2  *
3  * Copyright (c) Microsoft Corporation. 
4  *
5  * This source code is subject to terms and conditions of the Apache License, Version 2.0. A 
6  * copy of the license can be found in the License.html file at the root of this distribution. If 
7  * you cannot locate the  Apache License, Version 2.0, please send an email to 
8  * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound 
9  * by the terms of the Apache License, Version 2.0.
10  *
11  * You must not remove this notice, or any other, from this software.
12  *
13  *
14  * ***************************************************************************/
15
16 #if FEATURE_CORE_DLR
17 using System.Linq.Expressions;
18 #else
19 using Microsoft.Scripting.Ast;
20 #endif
21
22 using System;
23 using System.Collections.Generic;
24 using System.Diagnostics;
25 using System.Runtime.CompilerServices;
26 using Microsoft.Scripting.Utils;
27
28 namespace Microsoft.Scripting.Interpreter {
29     public sealed class LocalVariable {
30         private const int IsBoxedFlag = 1;
31         private const int InClosureFlag = 2;
32
33         public readonly int Index;
34         private int _flags;
35
36         public bool IsBoxed {
37             get { return (_flags & IsBoxedFlag) != 0; }
38             set {
39                 if (value) {
40                     _flags |= IsBoxedFlag;
41                 } else {
42                     _flags &= ~IsBoxedFlag;
43                 }
44             }
45         }
46
47         public bool InClosure {
48             get { return (_flags & InClosureFlag) != 0; }
49         }
50
51         public bool InClosureOrBoxed {
52             get { return InClosure | IsBoxed; }
53         }
54
55         internal LocalVariable(int index, bool closure, bool boxed) {
56             Index = index;
57             _flags = (closure ? InClosureFlag : 0) | (boxed ? IsBoxedFlag : 0);
58         }
59
60         internal Expression LoadFromArray(Expression frameData, Expression closure) {
61             Expression result = Expression.ArrayAccess(InClosure ? closure : frameData, Expression.Constant(Index));
62             return IsBoxed ? Expression.Convert(result, typeof(StrongBox<object>)) : result;
63         }
64
65         public override string ToString() {
66             return String.Format("{0}: {1} {2}", Index, IsBoxed ? "boxed" : null, InClosure ? "in closure" : null);
67         }
68     }
69
70     public struct LocalDefinition {
71         private readonly int _index;
72         private readonly ParameterExpression _parameter;
73
74         internal LocalDefinition(int localIndex, ParameterExpression parameter) {
75             _index = localIndex;
76             _parameter = parameter;
77         }
78
79         public int Index {
80             get {
81                 return _index;
82             }
83         }
84
85         public ParameterExpression Parameter {
86             get {
87                 return _parameter;
88             }
89         }
90
91         public override bool Equals(object obj) {
92             if (obj is LocalDefinition) {
93                 LocalDefinition other = (LocalDefinition)obj;
94                 return other.Index == Index && other.Parameter == Parameter;
95             }
96
97             return false;
98         }
99
100         public override int GetHashCode() {
101             if (_parameter == null) {
102                 return 0;
103             }
104             return _parameter.GetHashCode() ^ _index.GetHashCode();
105         }
106
107         public static bool operator ==(LocalDefinition self, LocalDefinition other) {
108             return self.Index == other.Index && self.Parameter == other.Parameter;
109         }
110
111         public static bool operator !=(LocalDefinition self, LocalDefinition other) {
112             return self.Index != other.Index || self.Parameter != other.Parameter;
113         }
114     }
115
116     public sealed class LocalVariables {
117         private readonly HybridReferenceDictionary<ParameterExpression, VariableScope> _variables = new HybridReferenceDictionary<ParameterExpression, VariableScope>();
118         private Dictionary<ParameterExpression, LocalVariable> _closureVariables;
119
120         private int _localCount, _maxLocalCount;
121
122         internal LocalVariables() {
123         }
124
125         public LocalDefinition DefineLocal(ParameterExpression variable, int start) {
126             ContractUtils.RequiresNotNull(variable, "variable");
127             ContractUtils.Requires(start >= 0, "start", "start must be positive");
128
129             LocalVariable result = new LocalVariable(_localCount++, false, false);
130             _maxLocalCount = System.Math.Max(_localCount, _maxLocalCount);
131
132             VariableScope existing, newScope;
133             if (_variables.TryGetValue(variable, out existing)) {
134                 newScope = new VariableScope(result, start, existing);
135                 if (existing.ChildScopes == null) {
136                     existing.ChildScopes = new List<VariableScope>();
137                 }
138                 existing.ChildScopes.Add(newScope);
139             } else {
140                 newScope = new VariableScope(result, start, null);
141             }
142
143             _variables[variable] = newScope;
144             return new LocalDefinition(result.Index, variable);
145         }
146
147         public void UndefineLocal(LocalDefinition definition, int end) {
148             var scope = _variables[definition.Parameter];
149             scope.Stop = end;
150             if (scope.Parent != null) {
151                 _variables[definition.Parameter] = scope.Parent;
152             } else {
153                 _variables.Remove(definition.Parameter);
154             }
155             
156             _localCount--;
157         }
158
159         internal void Box(ParameterExpression variable, InstructionList instructions) {
160             var scope = _variables[variable];
161
162             LocalVariable local = scope.Variable;
163             Debug.Assert(!local.IsBoxed && !local.InClosure);
164             _variables[variable].Variable.IsBoxed = true;
165                 
166             int curChild = 0;
167             for (int i = scope.Start; i < scope.Stop && i < instructions.Count; i++) {
168                 if (scope.ChildScopes != null && scope.ChildScopes[curChild].Start == i) {
169                     // skip boxing in the child scope
170                     var child = scope.ChildScopes[curChild];
171                     i = child.Stop;
172
173                     curChild++;
174                     continue;
175                 }
176
177                 instructions.SwitchToBoxed(local.Index, i);
178             }
179         }
180
181         public int LocalCount {
182             get { return _maxLocalCount; }
183         }
184
185         public int GetOrDefineLocal(ParameterExpression var) {
186             int index = GetLocalIndex(var);
187             if (index == -1) {
188                 return DefineLocal(var, 0).Index;
189             }
190             return index;
191         }
192
193         public int GetLocalIndex(ParameterExpression var) {
194             VariableScope loc;
195             return _variables.TryGetValue(var, out loc) ? loc.Variable.Index : -1;
196         }
197
198         public bool TryGetLocalOrClosure(ParameterExpression var, out LocalVariable local) {
199             VariableScope scope;
200             if (_variables.TryGetValue(var, out scope)) {
201                 local = scope.Variable;
202                 return true;
203             }
204             if (_closureVariables != null && _closureVariables.TryGetValue(var, out local)) {
205                 return true;
206             }
207
208             local = null;
209             return false;
210         }
211
212         /// <summary>
213         /// Gets a copy of the local variables which are defined in the current scope.
214         /// </summary>
215         /// <returns></returns>
216         internal Dictionary<ParameterExpression, LocalVariable> CopyLocals() {
217             var res = new Dictionary<ParameterExpression, LocalVariable>(_variables.Count);
218             foreach (var keyValue in _variables) {
219                 res[keyValue.Key] = keyValue.Value.Variable;
220             }
221             return res;
222         }
223
224         /// <summary>
225         /// Checks to see if the given variable is defined within the current local scope.
226         /// </summary>
227         internal bool ContainsVariable(ParameterExpression variable) {
228             return _variables.ContainsKey(variable);
229         }
230
231         /// <summary>
232         /// Gets the variables which are defined in an outer scope and available within the current scope.
233         /// </summary>
234         internal Dictionary<ParameterExpression, LocalVariable> ClosureVariables {
235             get {
236                 return _closureVariables;
237             }
238         }
239         
240         internal LocalVariable AddClosureVariable(ParameterExpression variable) {
241             if (_closureVariables == null) {
242                 _closureVariables = new Dictionary<ParameterExpression, LocalVariable>();
243             }
244             LocalVariable result = new LocalVariable(_closureVariables.Count, true, false);
245             _closureVariables.Add(variable, result);
246             return result;
247         }
248
249         /// <summary>
250         /// Tracks where a variable is defined and what range of instructions it's used in
251         /// </summary>
252         private sealed class VariableScope {
253             public readonly int Start;
254             public int Stop = Int32.MaxValue;
255             public readonly LocalVariable Variable;
256             public readonly VariableScope Parent;
257             public List<VariableScope> ChildScopes;
258
259             public VariableScope(LocalVariable variable, int start, VariableScope parent) {
260                 Variable = variable;
261                 Start = start;
262                 Parent = parent;
263             }
264         }
265     }
266 }