PageRenderTime 46ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/DLR/Microsoft.Dynamic/Interpreter/Instructions/LabelInfo.cs

#
C# | 303 lines | 195 code | 44 blank | 64 comment | 53 complexity | b2d31aa1773a5d0959b85f442bdc842d MD5 | raw file
Possible License(s): BSD-3-Clause
  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. using System.Linq.Expressions;
  16. using System;
  17. using System.Collections.Generic;
  18. using System.Diagnostics;
  19. using System.Linq;
  20. using System.Reflection.Emit;
  21. using System.Text;
  22. using Microsoft.Scripting.Utils;
  23. namespace Microsoft.Scripting.Interpreter {
  24. /// <summary>
  25. /// Contains compiler state corresponding to a LabelTarget
  26. /// See also LabelScopeInfo.
  27. /// </summary>
  28. internal sealed class LabelInfo {
  29. // The tree node representing this label
  30. private readonly LabelTarget _node;
  31. // The BranchLabel label, will be mutated if Node is redefined
  32. private BranchLabel _label;
  33. // The blocks where this label is defined. If it has more than one item,
  34. // the blocks can't be jumped to except from a child block
  35. // If there's only 1 block (the common case) it's stored here, if there's multiple blocks it's stored
  36. // as a HashSet<LabelScopeInfo>
  37. private object _definitions;
  38. // Blocks that jump to this block
  39. private readonly List<LabelScopeInfo> _references = new List<LabelScopeInfo>();
  40. // True if at least one jump is across blocks
  41. // If we have any jump across blocks to this label, then the
  42. // LabelTarget can only be defined in one place
  43. private bool _acrossBlockJump;
  44. internal LabelInfo(LabelTarget node) {
  45. _node = node;
  46. }
  47. internal BranchLabel GetLabel(LightCompiler compiler) {
  48. EnsureLabel(compiler);
  49. return _label;
  50. }
  51. internal void Reference(LabelScopeInfo block) {
  52. _references.Add(block);
  53. if (HasDefinitions) {
  54. ValidateJump(block);
  55. }
  56. }
  57. internal void Define(LabelScopeInfo block) {
  58. // Prevent the label from being shadowed, which enforces cleaner
  59. // trees. Also we depend on this for simplicity (keeping only one
  60. // active IL Label per LabelInfo)
  61. for (LabelScopeInfo j = block; j != null; j = j.Parent) {
  62. if (j.ContainsTarget(_node)) {
  63. throw new InvalidOperationException(String.Format("Label target already defined: {0}", _node.Name));
  64. }
  65. }
  66. AddDefinition(block);
  67. block.AddLabelInfo(_node, this);
  68. // Once defined, validate all jumps
  69. if (HasDefinitions && !HasMultipleDefinitions) {
  70. foreach (var r in _references) {
  71. ValidateJump(r);
  72. }
  73. } else {
  74. // Was just redefined, if we had any across block jumps, they're
  75. // now invalid
  76. if (_acrossBlockJump) {
  77. throw new InvalidOperationException("Ambiguous jump");
  78. }
  79. // For local jumps, we need a new IL label
  80. // This is okay because:
  81. // 1. no across block jumps have been made or will be made
  82. // 2. we don't allow the label to be shadowed
  83. _label = null;
  84. }
  85. }
  86. private void ValidateJump(LabelScopeInfo reference) {
  87. // look for a simple jump out
  88. for (LabelScopeInfo j = reference; j != null; j = j.Parent) {
  89. if (DefinedIn(j)) {
  90. // found it, jump is valid!
  91. return;
  92. }
  93. if (j.Kind == LabelScopeKind.Filter) {
  94. break;
  95. }
  96. }
  97. _acrossBlockJump = true;
  98. if (HasMultipleDefinitions) {
  99. throw new InvalidOperationException(String.Format("Ambiguous jump {0}", _node.Name));
  100. }
  101. // We didn't find an outward jump. Look for a jump across blocks
  102. LabelScopeInfo def = FirstDefinition();
  103. LabelScopeInfo common = CommonNode(def, reference, b => b.Parent);
  104. // Validate that we aren't jumping across a finally
  105. for (LabelScopeInfo j = reference; j != common; j = j.Parent) {
  106. if (j.Kind == LabelScopeKind.Filter) {
  107. throw new InvalidOperationException("Control cannot leave filter test");
  108. }
  109. }
  110. // Valdiate that we aren't jumping into a catch or an expression
  111. for (LabelScopeInfo j = def; j != common; j = j.Parent) {
  112. if (!j.CanJumpInto) {
  113. if (j.Kind == LabelScopeKind.Expression) {
  114. throw new InvalidOperationException("Control cannot enter an expression");
  115. } else {
  116. throw new InvalidOperationException("Control cannot enter try");
  117. }
  118. }
  119. }
  120. }
  121. internal void ValidateFinish() {
  122. // Make sure that if this label was jumped to, it is also defined
  123. if (_references.Count > 0 && !HasDefinitions) {
  124. throw new InvalidOperationException("label target undefined");
  125. }
  126. }
  127. private void EnsureLabel(LightCompiler compiler) {
  128. if (_label == null) {
  129. _label = compiler.Instructions.MakeLabel();
  130. }
  131. }
  132. private bool DefinedIn(LabelScopeInfo scope) {
  133. if (_definitions == scope) {
  134. return true;
  135. }
  136. HashSet<LabelScopeInfo> definitions = _definitions as HashSet<LabelScopeInfo>;
  137. if (definitions != null) {
  138. return definitions.Contains(scope);
  139. }
  140. return false;
  141. }
  142. private bool HasDefinitions {
  143. get {
  144. return _definitions != null;
  145. }
  146. }
  147. private LabelScopeInfo FirstDefinition() {
  148. LabelScopeInfo scope = _definitions as LabelScopeInfo;
  149. if (scope != null) {
  150. return scope;
  151. }
  152. return ((HashSet<LabelScopeInfo>)_definitions).First();
  153. }
  154. private void AddDefinition(LabelScopeInfo scope) {
  155. if (_definitions == null) {
  156. _definitions = scope;
  157. } else {
  158. HashSet<LabelScopeInfo> set = _definitions as HashSet<LabelScopeInfo>;
  159. if(set == null) {
  160. _definitions = set = new HashSet<LabelScopeInfo>() { (LabelScopeInfo)_definitions };
  161. }
  162. set.Add(scope);
  163. }
  164. }
  165. private bool HasMultipleDefinitions {
  166. get {
  167. return _definitions is HashSet<LabelScopeInfo>;
  168. }
  169. }
  170. internal static T CommonNode<T>(T first, T second, Func<T, T> parent) where T : class {
  171. var cmp = EqualityComparer<T>.Default;
  172. if (cmp.Equals(first, second)) {
  173. return first;
  174. }
  175. var set = new HashSet<T>(cmp);
  176. for (T t = first; t != null; t = parent(t)) {
  177. set.Add(t);
  178. }
  179. for (T t = second; t != null; t = parent(t)) {
  180. if (set.Contains(t)) {
  181. return t;
  182. }
  183. }
  184. return null;
  185. }
  186. }
  187. public enum LabelScopeKind {
  188. // any "statement like" node that can be jumped into
  189. Statement,
  190. // these correspond to the node of the same name
  191. Block,
  192. Switch,
  193. Lambda,
  194. Try,
  195. // these correspond to the part of the try block we're in
  196. Catch,
  197. Finally,
  198. Filter,
  199. // the catch-all value for any other expression type
  200. // (means we can't jump into it)
  201. Expression,
  202. }
  203. //
  204. // Tracks scoping information for LabelTargets. Logically corresponds to a
  205. // "label scope". Even though we have arbitrary goto support, we still need
  206. // to track what kinds of nodes that gotos are jumping through, both to
  207. // emit property IL ("leave" out of a try block), and for validation, and
  208. // to allow labels to be duplicated in the tree, as long as the jumps are
  209. // considered "up only" jumps.
  210. //
  211. // We create one of these for every Expression that can be jumped into, as
  212. // well as creating them for the first expression we can't jump into. The
  213. // "Kind" property indicates what kind of scope this is.
  214. //
  215. internal sealed class LabelScopeInfo {
  216. private HybridReferenceDictionary<LabelTarget, LabelInfo> Labels; // lazily allocated, we typically use this only once every 6th-7th block
  217. internal readonly LabelScopeKind Kind;
  218. internal readonly LabelScopeInfo Parent;
  219. internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) {
  220. Parent = parent;
  221. Kind = kind;
  222. }
  223. /// <summary>
  224. /// Returns true if we can jump into this node
  225. /// </summary>
  226. internal bool CanJumpInto {
  227. get {
  228. switch (Kind) {
  229. case LabelScopeKind.Block:
  230. case LabelScopeKind.Statement:
  231. case LabelScopeKind.Switch:
  232. case LabelScopeKind.Lambda:
  233. return true;
  234. }
  235. return false;
  236. }
  237. }
  238. internal bool ContainsTarget(LabelTarget target) {
  239. if (Labels == null) {
  240. return false;
  241. }
  242. return Labels.ContainsKey(target);
  243. }
  244. internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) {
  245. if (Labels == null) {
  246. info = null;
  247. return false;
  248. }
  249. return Labels.TryGetValue(target, out info);
  250. }
  251. internal void AddLabelInfo(LabelTarget target, LabelInfo info) {
  252. Debug.Assert(CanJumpInto);
  253. if (Labels == null) {
  254. Labels = new HybridReferenceDictionary<LabelTarget, LabelInfo>();
  255. }
  256. Labels[target] = info;
  257. }
  258. }
  259. }