1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
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.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using System.Linq.Expressions;
19 using Microsoft.Scripting.Ast;
23 using System.Collections.Generic;
24 using System.Diagnostics;
25 using System.Runtime.CompilerServices;
26 using Microsoft.Scripting.Utils;
28 namespace Microsoft.Scripting.Interpreter {
29 public sealed class LocalVariable {
30 private const int IsBoxedFlag = 1;
31 private const int InClosureFlag = 2;
33 public readonly int Index;
37 get { return (_flags & IsBoxedFlag) != 0; }
40 _flags |= IsBoxedFlag;
42 _flags &= ~IsBoxedFlag;
47 public bool InClosure {
48 get { return (_flags & InClosureFlag) != 0; }
51 public bool InClosureOrBoxed {
52 get { return InClosure | IsBoxed; }
55 internal LocalVariable(int index, bool closure, bool boxed) {
57 _flags = (closure ? InClosureFlag : 0) | (boxed ? IsBoxedFlag : 0);
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;
65 public override string ToString() {
66 return String.Format("{0}: {1} {2}", Index, IsBoxed ? "boxed" : null, InClosure ? "in closure" : null);
70 public struct LocalDefinition {
71 private readonly int _index;
72 private readonly ParameterExpression _parameter;
74 internal LocalDefinition(int localIndex, ParameterExpression parameter) {
76 _parameter = parameter;
85 public ParameterExpression Parameter {
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;
100 public override int GetHashCode() {
101 if (_parameter == null) {
104 return _parameter.GetHashCode() ^ _index.GetHashCode();
107 public static bool operator ==(LocalDefinition self, LocalDefinition other) {
108 return self.Index == other.Index && self.Parameter == other.Parameter;
111 public static bool operator !=(LocalDefinition self, LocalDefinition other) {
112 return self.Index != other.Index || self.Parameter != other.Parameter;
116 public sealed class LocalVariables {
117 private readonly HybridReferenceDictionary<ParameterExpression, VariableScope> _variables = new HybridReferenceDictionary<ParameterExpression, VariableScope>();
118 private Dictionary<ParameterExpression, LocalVariable> _closureVariables;
120 private int _localCount, _maxLocalCount;
122 internal LocalVariables() {
125 public LocalDefinition DefineLocal(ParameterExpression variable, int start) {
126 ContractUtils.RequiresNotNull(variable, "variable");
127 ContractUtils.Requires(start >= 0, "start", "start must be positive");
129 LocalVariable result = new LocalVariable(_localCount++, false, false);
130 _maxLocalCount = System.Math.Max(_localCount, _maxLocalCount);
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>();
138 existing.ChildScopes.Add(newScope);
140 newScope = new VariableScope(result, start, null);
143 _variables[variable] = newScope;
144 return new LocalDefinition(result.Index, variable);
147 public void UndefineLocal(LocalDefinition definition, int end) {
148 var scope = _variables[definition.Parameter];
150 if (scope.Parent != null) {
151 _variables[definition.Parameter] = scope.Parent;
153 _variables.Remove(definition.Parameter);
159 internal void Box(ParameterExpression variable, InstructionList instructions) {
160 var scope = _variables[variable];
162 LocalVariable local = scope.Variable;
163 Debug.Assert(!local.IsBoxed && !local.InClosure);
164 _variables[variable].Variable.IsBoxed = true;
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];
177 instructions.SwitchToBoxed(local.Index, i);
181 public int LocalCount {
182 get { return _maxLocalCount; }
185 public int GetOrDefineLocal(ParameterExpression var) {
186 int index = GetLocalIndex(var);
188 return DefineLocal(var, 0).Index;
193 public int GetLocalIndex(ParameterExpression var) {
195 return _variables.TryGetValue(var, out loc) ? loc.Variable.Index : -1;
198 public bool TryGetLocalOrClosure(ParameterExpression var, out LocalVariable local) {
200 if (_variables.TryGetValue(var, out scope)) {
201 local = scope.Variable;
204 if (_closureVariables != null && _closureVariables.TryGetValue(var, out local)) {
213 /// Gets a copy of the local variables which are defined in the current scope.
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;
225 /// Checks to see if the given variable is defined within the current local scope.
227 internal bool ContainsVariable(ParameterExpression variable) {
228 return _variables.ContainsKey(variable);
232 /// Gets the variables which are defined in an outer scope and available within the current scope.
234 internal Dictionary<ParameterExpression, LocalVariable> ClosureVariables {
236 return _closureVariables;
240 internal LocalVariable AddClosureVariable(ParameterExpression variable) {
241 if (_closureVariables == null) {
242 _closureVariables = new Dictionary<ParameterExpression, LocalVariable>();
244 LocalVariable result = new LocalVariable(_closureVariables.Count, true, false);
245 _closureVariables.Add(variable, result);
250 /// Tracks where a variable is defined and what range of instructions it's used in
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;
259 public VariableScope(LocalVariable variable, int start, VariableScope parent) {