/* **************************************************************************** * * Copyright (c) Microsoft Corporation. * * This source code is subject to terms and conditions of the Apache License, Version 2.0. A * copy of the license can be found in the License.html file at the root of this distribution. If * you cannot locate the Apache License, Version 2.0, please send an email to * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound * by the terms of the Apache License, Version 2.0. * * You must not remove this notice, or any other, from this software. * * * ***************************************************************************/ #if FEATURE_CORE_DLR using System.Linq.Expressions; #else using Microsoft.Scripting.Ast; #endif using System; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using Microsoft.Scripting.Utils; namespace Microsoft.Scripting.Interpreter { public sealed class LocalVariable { private const int IsBoxedFlag = 1; private const int InClosureFlag = 2; public readonly int Index; private int _flags; public bool IsBoxed { get { return (_flags & IsBoxedFlag) != 0; } set { if (value) { _flags |= IsBoxedFlag; } else { _flags &= ~IsBoxedFlag; } } } public bool InClosure { get { return (_flags & InClosureFlag) != 0; } } public bool InClosureOrBoxed { get { return InClosure | IsBoxed; } } internal LocalVariable(int index, bool closure, bool boxed) { Index = index; _flags = (closure ? InClosureFlag : 0) | (boxed ? IsBoxedFlag : 0); } internal Expression LoadFromArray(Expression frameData, Expression closure) { Expression result = Expression.ArrayAccess(InClosure ? closure : frameData, Expression.Constant(Index)); return IsBoxed ? Expression.Convert(result, typeof(StrongBox)) : result; } public override string ToString() { return String.Format("{0}: {1} {2}", Index, IsBoxed ? "boxed" : null, InClosure ? "in closure" : null); } } public struct LocalDefinition { private readonly int _index; private readonly ParameterExpression _parameter; internal LocalDefinition(int localIndex, ParameterExpression parameter) { _index = localIndex; _parameter = parameter; } public int Index { get { return _index; } } public ParameterExpression Parameter { get { return _parameter; } } public override bool Equals(object obj) { if (obj is LocalDefinition) { LocalDefinition other = (LocalDefinition)obj; return other.Index == Index && other.Parameter == Parameter; } return false; } public override int GetHashCode() { if (_parameter == null) { return 0; } return _parameter.GetHashCode() ^ _index.GetHashCode(); } public static bool operator ==(LocalDefinition self, LocalDefinition other) { return self.Index == other.Index && self.Parameter == other.Parameter; } public static bool operator !=(LocalDefinition self, LocalDefinition other) { return self.Index != other.Index || self.Parameter != other.Parameter; } } public sealed class LocalVariables { private readonly HybridReferenceDictionary _variables = new HybridReferenceDictionary(); private Dictionary _closureVariables; private int _localCount, _maxLocalCount; internal LocalVariables() { } public LocalDefinition DefineLocal(ParameterExpression variable, int start) { ContractUtils.RequiresNotNull(variable, "variable"); ContractUtils.Requires(start >= 0, "start", "start must be positive"); LocalVariable result = new LocalVariable(_localCount++, false, false); _maxLocalCount = System.Math.Max(_localCount, _maxLocalCount); VariableScope existing, newScope; if (_variables.TryGetValue(variable, out existing)) { newScope = new VariableScope(result, start, existing); if (existing.ChildScopes == null) { existing.ChildScopes = new List(); } existing.ChildScopes.Add(newScope); } else { newScope = new VariableScope(result, start, null); } _variables[variable] = newScope; return new LocalDefinition(result.Index, variable); } public void UndefineLocal(LocalDefinition definition, int end) { var scope = _variables[definition.Parameter]; scope.Stop = end; if (scope.Parent != null) { _variables[definition.Parameter] = scope.Parent; } else { _variables.Remove(definition.Parameter); } _localCount--; } internal void Box(ParameterExpression variable, InstructionList instructions) { var scope = _variables[variable]; LocalVariable local = scope.Variable; Debug.Assert(!local.IsBoxed && !local.InClosure); _variables[variable].Variable.IsBoxed = true; int curChild = 0; for (int i = scope.Start; i < scope.Stop && i < instructions.Count; i++) { if (scope.ChildScopes != null && scope.ChildScopes[curChild].Start == i) { // skip boxing in the child scope var child = scope.ChildScopes[curChild]; i = child.Stop; curChild++; continue; } instructions.SwitchToBoxed(local.Index, i); } } public int LocalCount { get { return _maxLocalCount; } } public int GetOrDefineLocal(ParameterExpression var) { int index = GetLocalIndex(var); if (index == -1) { return DefineLocal(var, 0).Index; } return index; } public int GetLocalIndex(ParameterExpression var) { VariableScope loc; return _variables.TryGetValue(var, out loc) ? loc.Variable.Index : -1; } public bool TryGetLocalOrClosure(ParameterExpression var, out LocalVariable local) { VariableScope scope; if (_variables.TryGetValue(var, out scope)) { local = scope.Variable; return true; } if (_closureVariables != null && _closureVariables.TryGetValue(var, out local)) { return true; } local = null; return false; } /// /// Gets a copy of the local variables which are defined in the current scope. /// /// internal Dictionary CopyLocals() { var res = new Dictionary(_variables.Count); foreach (var keyValue in _variables) { res[keyValue.Key] = keyValue.Value.Variable; } return res; } /// /// Checks to see if the given variable is defined within the current local scope. /// internal bool ContainsVariable(ParameterExpression variable) { return _variables.ContainsKey(variable); } /// /// Gets the variables which are defined in an outer scope and available within the current scope. /// internal Dictionary ClosureVariables { get { return _closureVariables; } } internal LocalVariable AddClosureVariable(ParameterExpression variable) { if (_closureVariables == null) { _closureVariables = new Dictionary(); } LocalVariable result = new LocalVariable(_closureVariables.Count, true, false); _closureVariables.Add(variable, result); return result; } /// /// Tracks where a variable is defined and what range of instructions it's used in /// private sealed class VariableScope { public readonly int Start; public int Stop = Int32.MaxValue; public readonly LocalVariable Variable; public readonly VariableScope Parent; public List ChildScopes; public VariableScope(LocalVariable variable, int start, VariableScope parent) { Variable = variable; Start = start; Parent = parent; } } } }