1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Microsoft Public License. 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 Microsoft Public License, 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 Microsoft Public License.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
15 using System; using Microsoft;
19 using ILGenerator = System.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
21 using ILGenerator = Microsoft.Linq.Expressions.Compiler.OffsetTrackingILGenerator;
24 using System.Collections.Generic;
25 using System.Diagnostics;
27 using System.Dynamic.Utils;
29 using Microsoft.Scripting.Utils;
31 using System.Reflection.Emit;
34 namespace System.Linq.Expressions.Compiler {
36 namespace Microsoft.Linq.Expressions.Compiler {
40 /// Contains compiler state corresponding to a LabelTarget
41 /// See also LabelScopeInfo.
43 internal sealed class LabelInfo {
44 // The tree node representing this label
45 private readonly LabelTarget _node;
47 // The IL label, will be mutated if Node is redefined
49 private bool _labelDefined;
51 internal Label Label {
53 EnsureLabelAndValue();
58 // The local that carries the label's value, if any
59 private LocalBuilder _value;
61 // The blocks where this label is defined. If it has more than one item,
62 // the blocks can't be jumped to except from a child block
63 private readonly Set<LabelScopeInfo> _definitions = new Set<LabelScopeInfo>();
65 // Blocks that jump to this block
66 private readonly List<LabelScopeInfo> _references = new List<LabelScopeInfo>();
68 // True if this label is the last thing in this block
69 // (meaning we can emit a direct return)
70 private readonly bool _canReturn;
72 // True if at least one jump is across blocks
73 // If we have any jump across blocks to this label, then the
74 // LabelTarget can only be defined in one place
75 private bool _acrossBlockJump;
77 // Until we have more information, default to a leave instruction,
78 // which always works. Note: leave spills the stack, so we need to
79 // ensure that StackSpiller has guarenteed us an empty stack at this
80 // point. Otherwise Leave and Branch are not equivalent
81 private OpCode _opCode = OpCodes.Leave;
83 private readonly ILGenerator _ilg;
85 internal LabelInfo(ILGenerator il, LabelTarget node, bool canReturn) {
88 _canReturn = canReturn;
91 internal bool CanReturn {
92 get { return _canReturn; }
96 /// Indicates if it is legal to emit a "branch" instruction based on
97 /// currently available information. Call the Reference method before
98 /// using this property.
100 internal bool CanBranch {
101 get { return _opCode != OpCodes.Leave; }
104 internal void Reference(LabelScopeInfo block) {
105 _references.Add(block);
106 if (_definitions.Count > 0) {
111 // Returns true if the label was successfully defined
112 // or false if the label is now ambiguous
113 internal void Define(LabelScopeInfo block) {
114 // Prevent the label from being shadowed, which enforces cleaner
115 // trees. Also we depend on this for simplicity (keeping only one
116 // active IL Label per LabelInfo)
117 for (LabelScopeInfo j = block; j != null; j = j.Parent) {
118 if (j.ContainsTarget(_node)) {
119 throw Error.LabelTargetAlreadyDefined(_node.Name);
123 _definitions.Add(block);
124 block.AddLabelInfo(_node, this);
126 // Once defined, validate all jumps
127 if (_definitions.Count == 1) {
128 foreach (var r in _references) {
132 // Was just redefined, if we had any across block jumps, they're
134 if (_acrossBlockJump) {
135 throw Error.AmbiguousJump(_node.Name);
137 // For local jumps, we need a new IL label
138 // This is okay because:
139 // 1. no across block jumps have been made or will be made
140 // 2. we don't allow the label to be shadowed
141 _labelDefined = false;
145 private void ValidateJump(LabelScopeInfo reference) {
146 // Assume we can do a ret/branch
147 _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
149 // look for a simple jump out
150 for (LabelScopeInfo j = reference; j != null; j = j.Parent) {
151 if (_definitions.Contains(j)) {
152 // found it, jump is valid!
155 if (j.Kind == LabelScopeKind.Finally ||
156 j.Kind == LabelScopeKind.Filter) {
159 if (j.Kind == LabelScopeKind.Try ||
160 j.Kind == LabelScopeKind.Catch) {
161 _opCode = OpCodes.Leave;
165 _acrossBlockJump = true;
166 if (_node != null && _node.Type != typeof(void)) {
167 throw Error.NonLocalJumpWithValue(_node.Name);
170 if (_definitions.Count > 1) {
171 throw Error.AmbiguousJump(_node.Name);
174 // We didn't find an outward jump. Look for a jump across blocks
175 LabelScopeInfo def = _definitions.First();
176 LabelScopeInfo common = Helpers.CommonNode(def, reference, b => b.Parent);
178 // Assume we can do a ret/branch
179 _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
181 // Validate that we aren't jumping across a finally
182 for (LabelScopeInfo j = reference; j != common; j = j.Parent) {
183 if (j.Kind == LabelScopeKind.Finally) {
184 throw Error.ControlCannotLeaveFinally();
186 if (j.Kind == LabelScopeKind.Filter) {
187 throw Error.ControlCannotLeaveFilterTest();
189 if (j.Kind == LabelScopeKind.Try ||
190 j.Kind == LabelScopeKind.Catch) {
191 _opCode = OpCodes.Leave;
195 // Valdiate that we aren't jumping into a catch or an expression
196 for (LabelScopeInfo j = def; j != common; j = j.Parent) {
197 if (!j.CanJumpInto) {
198 if (j.Kind == LabelScopeKind.Expression) {
199 throw Error.ControlCannotEnterExpression();
201 throw Error.ControlCannotEnterTry();
207 internal void ValidateFinish() {
208 // Make sure that if this label was jumped to, it is also defined
209 if (_references.Count > 0 && _definitions.Count == 0) {
210 throw Error.LabelTargetUndefined(_node.Name);
214 internal void EmitJump() {
215 // Return directly if we can
216 if (_opCode == OpCodes.Ret) {
217 _ilg.Emit(OpCodes.Ret);
220 _ilg.Emit(_opCode, Label);
224 private void StoreValue() {
225 EnsureLabelAndValue();
226 if (_value != null) {
227 _ilg.Emit(OpCodes.Stloc, _value);
231 internal void Mark() {
233 // Don't mark return labels unless they were actually jumped to
234 // (returns are last so we know for sure if anyone jumped to it)
235 if (!_labelDefined) {
236 // We don't even need to emit the "ret" because
237 // LambdaCompiler does that for us.
241 // Otherwise, emit something like:
245 _ilg.Emit(OpCodes.Ret);
248 // For the normal case, we emit:
254 MarkWithEmptyStack();
257 // Like Mark, but assumes the stack is empty
258 internal void MarkWithEmptyStack() {
259 _ilg.MarkLabel(Label);
260 if (_value != null) {
261 // We always read the value from a local, because we don't know
262 // if there will be a "leave" instruction targeting it ("branch"
263 // preserves its stack, but "leave" empties the stack)
264 _ilg.Emit(OpCodes.Ldloc, _value);
268 private void EnsureLabelAndValue() {
269 if (!_labelDefined) {
270 _labelDefined = true;
271 _label = _ilg.DefineLabel();
272 if (_node != null && _node.Type != typeof(void)) {
273 _value = _ilg.DeclareLocal(_node.Type);
279 internal enum LabelScopeKind {
280 // any "statement like" node that can be jumped into
283 // these correspond to the node of the same name
289 // these correspond to the part of the try block we're in
294 // the catch-all value for any other expression type
295 // (means we can't jump into it)
300 // Tracks scoping information for LabelTargets. Logically corresponds to a
301 // "label scope". Even though we have arbitrary goto support, we still need
302 // to track what kinds of nodes that gotos are jumping through, both to
303 // emit property IL ("leave" out of a try block), and for validation, and
304 // to allow labels to be duplicated in the tree, as long as the jumps are
305 // considered "up only" jumps.
307 // We create one of these for every Expression that can be jumped into, as
308 // well as creating them for the first expression we can't jump into. The
309 // "Kind" property indicates what kind of scope this is.
311 internal sealed class LabelScopeInfo {
312 private Dictionary<LabelTarget, LabelInfo> Labels; // lazily allocated, we typically use this only once every 6th-7th block
313 internal readonly LabelScopeKind Kind;
314 internal readonly LabelScopeInfo Parent;
316 internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) {
322 /// Returns true if we can jump into this node
324 internal bool CanJumpInto {
327 case LabelScopeKind.Block:
328 case LabelScopeKind.Statement:
329 case LabelScopeKind.Switch:
330 case LabelScopeKind.Lambda:
338 internal bool ContainsTarget(LabelTarget target) {
339 if (Labels == null) {
343 return Labels.ContainsKey(target);
346 internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) {
347 if (Labels == null) {
352 return Labels.TryGetValue(target, out info);
355 internal void AddLabelInfo(LabelTarget target, LabelInfo info) {
356 Debug.Assert(CanJumpInto);
358 if (Labels == null) {
359 Labels = new Dictionary<LabelTarget, LabelInfo>();
362 Labels.Add(target, info);