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;
27 using System.Reflection.Emit;
30 using Microsoft.Scripting.Utils;
32 namespace Microsoft.Scripting.Interpreter {
35 /// Contains compiler state corresponding to a LabelTarget
36 /// See also LabelScopeInfo.
38 internal sealed class LabelInfo {
39 // The tree node representing this label
40 private readonly LabelTarget _node;
42 // The BranchLabel label, will be mutated if Node is redefined
43 private BranchLabel _label;
45 // The blocks where this label is defined. If it has more than one item,
46 // the blocks can't be jumped to except from a child block
47 // If there's only 1 block (the common case) it's stored here, if there's multiple blocks it's stored
48 // as a HashSet<LabelScopeInfo>
49 private object _definitions;
51 // Blocks that jump to this block
52 private readonly List<LabelScopeInfo> _references = new List<LabelScopeInfo>();
54 // True if at least one jump is across blocks
55 // If we have any jump across blocks to this label, then the
56 // LabelTarget can only be defined in one place
57 private bool _acrossBlockJump;
59 internal LabelInfo(LabelTarget node) {
63 internal BranchLabel GetLabel(LightCompiler compiler) {
64 EnsureLabel(compiler);
68 internal void Reference(LabelScopeInfo block) {
69 _references.Add(block);
75 internal void Define(LabelScopeInfo block) {
76 // Prevent the label from being shadowed, which enforces cleaner
77 // trees. Also we depend on this for simplicity (keeping only one
78 // active IL Label per LabelInfo)
79 for (LabelScopeInfo j = block; j != null; j = j.Parent) {
80 if (j.ContainsTarget(_node)) {
81 throw new InvalidOperationException(String.Format("Label target already defined: {0}", _node.Name));
86 block.AddLabelInfo(_node, this);
88 // Once defined, validate all jumps
89 if (HasDefinitions && !HasMultipleDefinitions) {
90 foreach (var r in _references) {
94 // Was just redefined, if we had any across block jumps, they're
96 if (_acrossBlockJump) {
97 throw new InvalidOperationException("Ambiguous jump");
99 // For local jumps, we need a new IL label
100 // This is okay because:
101 // 1. no across block jumps have been made or will be made
102 // 2. we don't allow the label to be shadowed
107 private void ValidateJump(LabelScopeInfo reference) {
108 // look for a simple jump out
109 for (LabelScopeInfo j = reference; j != null; j = j.Parent) {
111 // found it, jump is valid!
114 if (j.Kind == LabelScopeKind.Filter) {
119 _acrossBlockJump = true;
121 if (HasMultipleDefinitions) {
122 throw new InvalidOperationException(String.Format("Ambiguous jump {0}", _node.Name));
125 // We didn't find an outward jump. Look for a jump across blocks
126 LabelScopeInfo def = FirstDefinition();
127 LabelScopeInfo common = CommonNode(def, reference, b => b.Parent);
129 // Validate that we aren't jumping across a finally
130 for (LabelScopeInfo j = reference; j != common; j = j.Parent) {
131 if (j.Kind == LabelScopeKind.Filter) {
132 throw new InvalidOperationException("Control cannot leave filter test");
136 // Valdiate that we aren't jumping into a catch or an expression
137 for (LabelScopeInfo j = def; j != common; j = j.Parent) {
138 if (!j.CanJumpInto) {
139 if (j.Kind == LabelScopeKind.Expression) {
140 throw new InvalidOperationException("Control cannot enter an expression");
142 throw new InvalidOperationException("Control cannot enter try");
148 internal void ValidateFinish() {
149 // Make sure that if this label was jumped to, it is also defined
150 if (_references.Count > 0 && !HasDefinitions) {
151 throw new InvalidOperationException("label target undefined");
155 private void EnsureLabel(LightCompiler compiler) {
156 if (_label == null) {
157 _label = compiler.Instructions.MakeLabel();
161 private bool DefinedIn(LabelScopeInfo scope) {
162 if (_definitions == scope) {
166 HashSet<LabelScopeInfo> definitions = _definitions as HashSet<LabelScopeInfo>;
167 if (definitions != null) {
168 return definitions.Contains(scope);
173 private bool HasDefinitions {
175 return _definitions != null;
179 private LabelScopeInfo FirstDefinition() {
180 LabelScopeInfo scope = _definitions as LabelScopeInfo;
184 return ((HashSet<LabelScopeInfo>)_definitions).First();
187 private void AddDefinition(LabelScopeInfo scope) {
188 if (_definitions == null) {
189 _definitions = scope;
191 HashSet<LabelScopeInfo> set = _definitions as HashSet<LabelScopeInfo>;
193 _definitions = set = new HashSet<LabelScopeInfo>() { (LabelScopeInfo)_definitions };
199 private bool HasMultipleDefinitions {
201 return _definitions is HashSet<LabelScopeInfo>;
205 internal static T CommonNode<T>(T first, T second, Func<T, T> parent) where T : class {
206 var cmp = EqualityComparer<T>.Default;
207 if (cmp.Equals(first, second)) {
210 var set = new HashSet<T>(cmp);
211 for (T t = first; t != null; t = parent(t)) {
214 for (T t = second; t != null; t = parent(t)) {
215 if (set.Contains(t)) {
223 public enum LabelScopeKind {
224 // any "statement like" node that can be jumped into
227 // these correspond to the node of the same name
233 // these correspond to the part of the try block we're in
238 // the catch-all value for any other expression type
239 // (means we can't jump into it)
244 // Tracks scoping information for LabelTargets. Logically corresponds to a
245 // "label scope". Even though we have arbitrary goto support, we still need
246 // to track what kinds of nodes that gotos are jumping through, both to
247 // emit property IL ("leave" out of a try block), and for validation, and
248 // to allow labels to be duplicated in the tree, as long as the jumps are
249 // considered "up only" jumps.
251 // We create one of these for every Expression that can be jumped into, as
252 // well as creating them for the first expression we can't jump into. The
253 // "Kind" property indicates what kind of scope this is.
255 internal sealed class LabelScopeInfo {
256 private HybridReferenceDictionary<LabelTarget, LabelInfo> Labels; // lazily allocated, we typically use this only once every 6th-7th block
257 internal readonly LabelScopeKind Kind;
258 internal readonly LabelScopeInfo Parent;
260 internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) {
266 /// Returns true if we can jump into this node
268 internal bool CanJumpInto {
271 case LabelScopeKind.Block:
272 case LabelScopeKind.Statement:
273 case LabelScopeKind.Switch:
274 case LabelScopeKind.Lambda:
282 internal bool ContainsTarget(LabelTarget target) {
283 if (Labels == null) {
287 return Labels.ContainsKey(target);
290 internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) {
291 if (Labels == null) {
296 return Labels.TryGetValue(target, out info);
299 internal void AddLabelInfo(LabelTarget target, LabelInfo info) {
300 Debug.Assert(CanJumpInto);
302 if (Labels == null) {
303 Labels = new HybridReferenceDictionary<LabelTarget, LabelInfo>();
306 Labels[target] = info;