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.Collections.Generic;
18 using System.Diagnostics;
19 using System.Dynamic.Utils;
20 using System.Reflection.Emit;
27 namespace Microsoft.Scripting.Ast.Compiler {
29 namespace System.Linq.Expressions.Compiler {
31 #if CLR2 || SILVERLIGHT
32 using ILGenerator = OffsetTrackingILGenerator;
36 /// Contains compiler state corresponding to a LabelTarget
37 /// See also LabelScopeInfo.
39 internal sealed class LabelInfo {
40 // The tree node representing this label
41 private readonly LabelTarget _node;
43 // The IL label, will be mutated if Node is redefined
45 private bool _labelDefined;
47 internal Label Label {
49 EnsureLabelAndValue();
54 // The local that carries the label's value, if any
55 private LocalBuilder _value;
57 // The blocks where this label is defined. If it has more than one item,
58 // the blocks can't be jumped to except from a child block
59 private readonly Set<LabelScopeInfo> _definitions = new Set<LabelScopeInfo>();
61 // Blocks that jump to this block
62 private readonly List<LabelScopeInfo> _references = new List<LabelScopeInfo>();
64 // True if this label is the last thing in this block
65 // (meaning we can emit a direct return)
66 private readonly bool _canReturn;
68 // True if at least one jump is across blocks
69 // If we have any jump across blocks to this label, then the
70 // LabelTarget can only be defined in one place
71 private bool _acrossBlockJump;
73 // Until we have more information, default to a leave instruction,
74 // which always works. Note: leave spills the stack, so we need to
75 // ensure that StackSpiller has guarenteed us an empty stack at this
76 // point. Otherwise Leave and Branch are not equivalent
77 private OpCode _opCode = OpCodes.Leave;
79 private readonly ILGenerator _ilg;
81 internal LabelInfo(ILGenerator il, LabelTarget node, bool canReturn) {
84 _canReturn = canReturn;
87 internal bool CanReturn {
88 get { return _canReturn; }
92 /// Indicates if it is legal to emit a "branch" instruction based on
93 /// currently available information. Call the Reference method before
94 /// using this property.
96 internal bool CanBranch {
97 get { return _opCode != OpCodes.Leave; }
100 internal void Reference(LabelScopeInfo block) {
101 _references.Add(block);
102 if (_definitions.Count > 0) {
107 // Returns true if the label was successfully defined
108 // or false if the label is now ambiguous
109 internal void Define(LabelScopeInfo block) {
110 // Prevent the label from being shadowed, which enforces cleaner
111 // trees. Also we depend on this for simplicity (keeping only one
112 // active IL Label per LabelInfo)
113 for (LabelScopeInfo j = block; j != null; j = j.Parent) {
114 if (j.ContainsTarget(_node)) {
115 throw Error.LabelTargetAlreadyDefined(_node.Name);
119 _definitions.Add(block);
120 block.AddLabelInfo(_node, this);
122 // Once defined, validate all jumps
123 if (_definitions.Count == 1) {
124 foreach (var r in _references) {
128 // Was just redefined, if we had any across block jumps, they're
130 if (_acrossBlockJump) {
131 throw Error.AmbiguousJump(_node.Name);
133 // For local jumps, we need a new IL label
134 // This is okay because:
135 // 1. no across block jumps have been made or will be made
136 // 2. we don't allow the label to be shadowed
137 _labelDefined = false;
141 private void ValidateJump(LabelScopeInfo reference) {
142 // Assume we can do a ret/branch
143 _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
145 // look for a simple jump out
146 for (LabelScopeInfo j = reference; j != null; j = j.Parent) {
147 if (_definitions.Contains(j)) {
148 // found it, jump is valid!
151 if (j.Kind == LabelScopeKind.Finally ||
152 j.Kind == LabelScopeKind.Filter) {
155 if (j.Kind == LabelScopeKind.Try ||
156 j.Kind == LabelScopeKind.Catch) {
157 _opCode = OpCodes.Leave;
161 _acrossBlockJump = true;
162 if (_node != null && _node.Type != typeof(void)) {
163 throw Error.NonLocalJumpWithValue(_node.Name);
166 if (_definitions.Count > 1) {
167 throw Error.AmbiguousJump(_node.Name);
170 // We didn't find an outward jump. Look for a jump across blocks
171 LabelScopeInfo def = _definitions.First();
172 LabelScopeInfo common = Helpers.CommonNode(def, reference, b => b.Parent);
174 // Assume we can do a ret/branch
175 _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
177 // Validate that we aren't jumping across a finally
178 for (LabelScopeInfo j = reference; j != common; j = j.Parent) {
179 if (j.Kind == LabelScopeKind.Finally) {
180 throw Error.ControlCannotLeaveFinally();
182 if (j.Kind == LabelScopeKind.Filter) {
183 throw Error.ControlCannotLeaveFilterTest();
185 if (j.Kind == LabelScopeKind.Try ||
186 j.Kind == LabelScopeKind.Catch) {
187 _opCode = OpCodes.Leave;
191 // Valdiate that we aren't jumping into a catch or an expression
192 for (LabelScopeInfo j = def; j != common; j = j.Parent) {
193 if (!j.CanJumpInto) {
194 if (j.Kind == LabelScopeKind.Expression) {
195 throw Error.ControlCannotEnterExpression();
197 throw Error.ControlCannotEnterTry();
203 internal void ValidateFinish() {
204 // Make sure that if this label was jumped to, it is also defined
205 if (_references.Count > 0 && _definitions.Count == 0) {
206 throw Error.LabelTargetUndefined(_node.Name);
210 internal void EmitJump() {
211 // Return directly if we can
212 if (_opCode == OpCodes.Ret) {
213 _ilg.Emit(OpCodes.Ret);
216 _ilg.Emit(_opCode, Label);
220 private void StoreValue() {
221 EnsureLabelAndValue();
222 if (_value != null) {
223 _ilg.Emit(OpCodes.Stloc, _value);
227 internal void Mark() {
229 // Don't mark return labels unless they were actually jumped to
230 // (returns are last so we know for sure if anyone jumped to it)
231 if (!_labelDefined) {
232 // We don't even need to emit the "ret" because
233 // LambdaCompiler does that for us.
237 // Otherwise, emit something like:
241 _ilg.Emit(OpCodes.Ret);
244 // For the normal case, we emit:
250 MarkWithEmptyStack();
253 // Like Mark, but assumes the stack is empty
254 internal void MarkWithEmptyStack() {
255 _ilg.MarkLabel(Label);
256 if (_value != null) {
257 // We always read the value from a local, because we don't know
258 // if there will be a "leave" instruction targeting it ("branch"
259 // preserves its stack, but "leave" empties the stack)
260 _ilg.Emit(OpCodes.Ldloc, _value);
264 private void EnsureLabelAndValue() {
265 if (!_labelDefined) {
266 _labelDefined = true;
267 _label = _ilg.DefineLabel();
268 if (_node != null && _node.Type != typeof(void)) {
269 _value = _ilg.DeclareLocal(_node.Type);
275 internal enum LabelScopeKind {
276 // any "statement like" node that can be jumped into
279 // these correspond to the node of the same name
285 // these correspond to the part of the try block we're in
290 // the catch-all value for any other expression type
291 // (means we can't jump into it)
296 // Tracks scoping information for LabelTargets. Logically corresponds to a
297 // "label scope". Even though we have arbitrary goto support, we still need
298 // to track what kinds of nodes that gotos are jumping through, both to
299 // emit property IL ("leave" out of a try block), and for validation, and
300 // to allow labels to be duplicated in the tree, as long as the jumps are
301 // considered "up only" jumps.
303 // We create one of these for every Expression that can be jumped into, as
304 // well as creating them for the first expression we can't jump into. The
305 // "Kind" property indicates what kind of scope this is.
307 internal sealed class LabelScopeInfo {
308 private Dictionary<LabelTarget, LabelInfo> Labels; // lazily allocated, we typically use this only once every 6th-7th block
309 internal readonly LabelScopeKind Kind;
310 internal readonly LabelScopeInfo Parent;
312 internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) {
318 /// Returns true if we can jump into this node
320 internal bool CanJumpInto {
323 case LabelScopeKind.Block:
324 case LabelScopeKind.Statement:
325 case LabelScopeKind.Switch:
326 case LabelScopeKind.Lambda:
334 internal bool ContainsTarget(LabelTarget target) {
335 if (Labels == null) {
339 return Labels.ContainsKey(target);
342 internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) {
343 if (Labels == null) {
348 return Labels.TryGetValue(target, out info);
351 internal void AddLabelInfo(LabelTarget target, LabelInfo info) {
352 Debug.Assert(CanJumpInto);
354 if (Labels == null) {
355 Labels = new Dictionary<LabelTarget, LabelInfo>();
358 Labels.Add(target, info);