LabelInfo.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / Microsoft / Scripting / Compiler / LabelInfo.cs / 1305376 / LabelInfo.cs

                            /* **************************************************************************** 
 *
 * Copyright (c) Microsoft Corporation.
 *
 * This source code is subject to terms and conditions of the Microsoft Public License. A 
 * copy of the license can be found in the License.html file at the root of this distribution. If
 * you cannot locate the  Microsoft Public License, 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 Microsoft Public License.
 * 
 * You must not remove this notice, or any other, from this software.
 *
 *
 * ***************************************************************************/ 

#if MICROSOFT_SCRIPTING_CORE || SILVERLIGHT 
using ILGenerator = System.Linq.Expressions.Compiler.OffsetTrackingILGenerator; 
#endif
 
using System.Collections.Generic;
using System.Diagnostics;
using System.Dynamic.Utils;
using System.Reflection.Emit; 

#if SILVERLIGHT 
using System.Core; 
#endif
 
namespace System.Linq.Expressions.Compiler {

    /// 
    /// Contains compiler state corresponding to a LabelTarget 
    /// See also LabelScopeInfo.
    ///  
    internal sealed class LabelInfo { 
        // The tree node representing this label
        private readonly LabelTarget _node; 

        // The IL label, will be mutated if Node is redefined
        private Label _label;
        private bool _labelDefined; 

        internal Label Label { 
            get { 
                EnsureLabelAndValue();
                return _label; 
            }
        }

        // The local that carries the label's value, if any 
        private LocalBuilder _value;
 
        // The blocks where this label is defined. If it has more than one item, 
        // the blocks can't be jumped to except from a child block
        private readonly Set _definitions = new Set(); 

        // Blocks that jump to this block
        private readonly List _references = new List();
 
        // True if this label is the last thing in this block
        // (meaning we can emit a direct return) 
        private readonly bool _canReturn; 

        // True if at least one jump is across blocks 
        // If we have any jump across blocks to this label, then the
        // LabelTarget can only be defined in one place
        private bool _acrossBlockJump;
 
        // Until we have more information, default to a leave instruction,
        // which always works. Note: leave spills the stack, so we need to 
        // ensure that StackSpiller has guarenteed us an empty stack at this 
        // point. Otherwise Leave and Branch are not equivalent
        private OpCode _opCode = OpCodes.Leave; 

        private readonly ILGenerator _ilg;

        internal LabelInfo(ILGenerator il, LabelTarget node, bool canReturn) { 
            _ilg = il;
            _node = node; 
            _canReturn = canReturn; 
        }
 
        internal bool CanReturn {
            get { return _canReturn; }
        }
 
        /// 
        /// Indicates if it is legal to emit a "branch" instruction based on 
        /// currently available information. Call the Reference method before 
        /// using this property.
        ///  
        internal bool CanBranch {
            get { return _opCode != OpCodes.Leave; }
        }
 
        internal void Reference(LabelScopeInfo block) {
            _references.Add(block); 
            if (_definitions.Count > 0) { 
                ValidateJump(block);
            } 
        }

        // Returns true if the label was successfully defined
        // or false if the label is now ambiguous 
        internal void Define(LabelScopeInfo block) {
            // Prevent the label from being shadowed, which enforces cleaner 
            // trees. Also we depend on this for simplicity (keeping only one 
            // active IL Label per LabelInfo)
            for (LabelScopeInfo j = block; j != null; j = j.Parent) { 
                if (j.ContainsTarget(_node)) {
                    throw Error.LabelTargetAlreadyDefined(_node.Name);
                }
            } 

            _definitions.Add(block); 
            block.AddLabelInfo(_node, this); 

            // Once defined, validate all jumps 
            if (_definitions.Count == 1) {
                foreach (var r in _references) {
                    ValidateJump(r);
                } 
            } else {
                // Was just redefined, if we had any across block jumps, they're 
                // now invalid 
                if (_acrossBlockJump) {
                    throw Error.AmbiguousJump(_node.Name); 
                }
                // For local jumps, we need a new IL label
                // This is okay because:
                //   1. no across block jumps have been made or will be made 
                //   2. we don't allow the label to be shadowed
                _labelDefined = false; 
            } 
        }
 
        private void ValidateJump(LabelScopeInfo reference) {
            // Assume we can do a ret/branch
            _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
 
            // look for a simple jump out
            for (LabelScopeInfo j = reference; j != null; j = j.Parent) { 
                if (_definitions.Contains(j)) { 
                    // found it, jump is valid!
                    return; 
                }
                if (j.Kind == LabelScopeKind.Finally ||
                    j.Kind == LabelScopeKind.Filter) {
                    break; 
                }
                if (j.Kind == LabelScopeKind.Try || 
                    j.Kind == LabelScopeKind.Catch) { 
                    _opCode = OpCodes.Leave;
                } 
            }

            _acrossBlockJump = true;
            if (_node != null && _node.Type != typeof(void)) { 
                throw Error.NonLocalJumpWithValue(_node.Name);
            } 
 
            if (_definitions.Count > 1) {
                throw Error.AmbiguousJump(_node.Name); 
            }

            // We didn't find an outward jump. Look for a jump across blocks
            LabelScopeInfo def = _definitions.First(); 
            LabelScopeInfo common = Helpers.CommonNode(def, reference, b => b.Parent);
 
            // Assume we can do a ret/branch 
            _opCode = _canReturn ? OpCodes.Ret : OpCodes.Br;
 
            // Validate that we aren't jumping across a finally
            for (LabelScopeInfo j = reference; j != common; j = j.Parent) {
                if (j.Kind == LabelScopeKind.Finally) {
                    throw Error.ControlCannotLeaveFinally(); 
                }
                if (j.Kind == LabelScopeKind.Filter) { 
                    throw Error.ControlCannotLeaveFilterTest(); 
                }
                if (j.Kind == LabelScopeKind.Try || 
                    j.Kind == LabelScopeKind.Catch) {
                    _opCode = OpCodes.Leave;
                }
            } 

            // Valdiate that we aren't jumping into a catch or an expression 
            for (LabelScopeInfo j = def; j != common; j = j.Parent) { 
                if (!j.CanJumpInto) {
                    if (j.Kind == LabelScopeKind.Expression) { 
                        throw Error.ControlCannotEnterExpression();
                    } else {
                        throw Error.ControlCannotEnterTry();
                    } 
                }
            } 
        } 

        internal void ValidateFinish() { 
            // Make sure that if this label was jumped to, it is also defined
            if (_references.Count > 0 && _definitions.Count == 0) {
                throw Error.LabelTargetUndefined(_node.Name);
            } 
        }
 
        internal void EmitJump() { 
            // Return directly if we can
            if (_opCode == OpCodes.Ret) { 
                _ilg.Emit(OpCodes.Ret);
            } else {
                StoreValue();
                _ilg.Emit(_opCode, Label); 
            }
        } 
 
        private void StoreValue() {
            EnsureLabelAndValue(); 
            if (_value != null) {
                _ilg.Emit(OpCodes.Stloc, _value);
            }
        } 

        internal void Mark() { 
            if (_canReturn) { 
                // Don't mark return labels unless they were actually jumped to
                // (returns are last so we know for sure if anyone jumped to it) 
                if (!_labelDefined) {
                    // We don't even need to emit the "ret" because
                    // LambdaCompiler does that for us.
                    return; 
                }
 
                // Otherwise, emit something like: 
                // ret
                // : 
                // ldloc 
                _ilg.Emit(OpCodes.Ret);
            } else {
 
                // For the normal case, we emit:
                // stloc  
                // : 
                // ldloc 
                StoreValue(); 
            }
            MarkWithEmptyStack();
        }
 
        // Like Mark, but assumes the stack is empty
        internal void MarkWithEmptyStack() { 
            _ilg.MarkLabel(Label); 
            if (_value != null) {
                // We always read the value from a local, because we don't know 
                // if there will be a "leave" instruction targeting it ("branch"
                // preserves its stack, but "leave" empties the stack)
                _ilg.Emit(OpCodes.Ldloc, _value);
            } 
        }
 
        private void EnsureLabelAndValue() { 
            if (!_labelDefined) {
                _labelDefined = true; 
                _label = _ilg.DefineLabel();
                if (_node != null && _node.Type != typeof(void)) {
                    _value = _ilg.DeclareLocal(_node.Type);
                } 
            }
        } 
    } 

    internal enum LabelScopeKind { 
        // any "statement like" node that can be jumped into
        Statement,

        // these correspond to the node of the same name 
        Block,
        Switch, 
        Lambda, 
        Try,
 
        // these correspond to the part of the try block we're in
        Catch,
        Finally,
        Filter, 

        // the catch-all value for any other expression type 
        // (means we can't jump into it) 
        Expression,
    } 

    //
    // Tracks scoping information for LabelTargets. Logically corresponds to a
    // "label scope". Even though we have arbitrary goto support, we still need 
    // to track what kinds of nodes that gotos are jumping through, both to
    // emit property IL ("leave" out of a try block), and for validation, and 
    // to allow labels to be duplicated in the tree, as long as the jumps are 
    // considered "up only" jumps.
    // 
    // We create one of these for every Expression that can be jumped into, as
    // well as creating them for the first expression we can't jump into. The
    // "Kind" property indicates what kind of scope this is.
    // 
    internal sealed class LabelScopeInfo {
        private Dictionary Labels; // lazily allocated, we typically use this only once every 6th-7th block 
        internal readonly LabelScopeKind Kind; 
        internal readonly LabelScopeInfo Parent;
 
        internal LabelScopeInfo(LabelScopeInfo parent, LabelScopeKind kind) {
            Parent = parent;
            Kind = kind;
        } 

        ///  
        /// Returns true if we can jump into this node 
        /// 
        internal bool CanJumpInto { 
            get {
                switch (Kind) {
                    case LabelScopeKind.Block:
                    case LabelScopeKind.Statement: 
                    case LabelScopeKind.Switch:
                    case LabelScopeKind.Lambda: 
                        return true; 
                }
                return false; 
            }
        }

 
        internal bool ContainsTarget(LabelTarget target) {
            if (Labels == null) { 
                return false; 
            }
 
            return Labels.ContainsKey(target);
        }

        internal bool TryGetLabelInfo(LabelTarget target, out LabelInfo info) { 
            if (Labels == null) {
                info = null; 
                return false; 
            }
 
            return Labels.TryGetValue(target, out info);
        }

        internal void AddLabelInfo(LabelTarget target, LabelInfo info) { 
            Debug.Assert(CanJumpInto);
 
            if (Labels == null) { 
                Labels = new Dictionary();
            } 

            Labels.Add(target, info);
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK