LambdaCompiler.ControlFlow.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / Microsoft / Scripting / Compiler / LambdaCompiler.ControlFlow.cs / 1305376 / LambdaCompiler.ControlFlow.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.
 *
 *
 * ***************************************************************************/ 

using System.Diagnostics; 
using System.Dynamic.Utils; 

namespace System.Linq.Expressions.Compiler { 

    // The part of the LambdaCompiler dealing with low level control flow
    // break, contiue, return, exceptions, etc
    partial class LambdaCompiler { 

        private LabelInfo EnsureLabel(LabelTarget node) { 
            LabelInfo result; 
            if (!_labelInfo.TryGetValue(node, out result)) {
                _labelInfo.Add(node, result = new LabelInfo(_ilg, node, false)); 
            }
            return result;
        }
 
        private LabelInfo ReferenceLabel(LabelTarget node) {
            LabelInfo result = EnsureLabel(node); 
            result.Reference(_labelBlock); 
            return result;
        } 

        private LabelInfo DefineLabel(LabelTarget node) {
            if (node == null) {
                return new LabelInfo(_ilg, null, false); 
            }
            LabelInfo result = EnsureLabel(node); 
            result.Define(_labelBlock); 
            return result;
        } 

        private void PushLabelBlock(LabelScopeKind type) {
            _labelBlock = new LabelScopeInfo(_labelBlock, type);
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")] 
        private void PopLabelBlock(LabelScopeKind kind) { 
            Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind);
            _labelBlock = _labelBlock.Parent; 
        }

        private void EmitLabelExpression(Expression expr, CompilationFlags flags) {
            var node = (LabelExpression)expr; 
            Debug.Assert(node.Target != null);
 
            // If we're an immediate child of a block, our label will already 
            // be defined. If not, we need to define our own block so this
            // label isn't exposed except to its own child expression. 
            LabelInfo label = null;

            if (_labelBlock.Kind == LabelScopeKind.Block) {
                _labelBlock.TryGetLabelInfo(node.Target, out label); 

                // We're in a block but didn't find our label, try switch 
                if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch) { 
                    _labelBlock.Parent.TryGetLabelInfo(node.Target, out label);
                } 

                // if we're in a switch or block, we should've found the label
                Debug.Assert(label != null);
            } 

            if (label == null) { 
                label = DefineLabel(node.Target); 
            }
 
            if (node.DefaultValue != null) {
                if (node.Target.Type == typeof(void)) {
                    EmitExpressionAsVoid(node.DefaultValue, flags);
                } else { 
                    flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
                    EmitExpression(node.DefaultValue, flags); 
                } 
            }
 
            label.Mark();
        }

        private void EmitGotoExpression(Expression expr, CompilationFlags flags) { 
            var node = (GotoExpression)expr;
            var labelInfo = ReferenceLabel(node.Target); 
 
            var tailCall = flags & CompilationFlags.EmitAsTailCallMask;
            if (tailCall != CompilationFlags.EmitAsNoTail) { 
                // Since tail call flags are not passed into EmitTryExpression, CanReturn
                // means the goto will be emitted as Ret. Therefore we can emit the goto's
                // default value with tail call. This can be improved by detecting if the
                // target label is equivalent to the return label. 
                tailCall = labelInfo.CanReturn ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
                flags = UpdateEmitAsTailCallFlag(flags, tailCall); 
            } 

            if (node.Value != null) { 
                if (node.Target.Type == typeof(void)) {
                    EmitExpressionAsVoid(node.Value, flags);
                } else {
                    flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart); 
                    EmitExpression(node.Value, flags);
                } 
            } 

            labelInfo.EmitJump(); 

            EmitUnreachable(node, flags);
        }
 
        // We need to push default(T), unless we're emitting ourselves as
        // void. Even though the code is unreachable, we still have to 
        // generate correct IL. We can get rid of this once we have better 
        // reachability analysis.
        private void EmitUnreachable(Expression node, CompilationFlags flags) { 
            if (node.Type != typeof(void) && (flags & CompilationFlags.EmitAsVoidType) == 0) {
                _ilg.EmitDefault(node.Type);
            }
        } 

        private bool TryPushLabelBlock(Expression node) { 
            // Anything that is "statement-like" -- e.g. has no associated 
            // stack state can be jumped into, with the exception of try-blocks
            // We indicate this by a "Block" 
            //
            // Otherwise, we push an "Expression" to indicate that it can't be
            // jumped into
            switch (node.NodeType) { 
                default:
                    if (_labelBlock.Kind != LabelScopeKind.Expression) { 
                        PushLabelBlock(LabelScopeKind.Expression); 
                        return true;
                    } 
                    return false;
                case ExpressionType.Label:
                    // LabelExpression is a bit special, if it's directly in a
                    // block it becomes associate with the block's scope. Same 
                    // thing if it's in a switch case body.
                    if (_labelBlock.Kind == LabelScopeKind.Block) { 
                        var label = ((LabelExpression)node).Target; 
                        if (_labelBlock.ContainsTarget(label)) {
                            return false; 
                        }
                        if (_labelBlock.Parent.Kind == LabelScopeKind.Switch &&
                            _labelBlock.Parent.ContainsTarget(label)) {
                            return false; 
                        }
                    } 
                    PushLabelBlock(LabelScopeKind.Statement); 
                    return true;
                case ExpressionType.Block: 
                    if (node is SpilledExpressionBlock) {
                        // treat it as an expression
                        goto default;
                    } 

                    PushLabelBlock(LabelScopeKind.Block); 
                    // Labels defined immediately in the block are valid for 
                    // the whole block.
                    if (_labelBlock.Parent.Kind != LabelScopeKind.Switch) { 
                        DefineBlockLabels(node);
                    }
                    return true;
                case ExpressionType.Switch: 
                    PushLabelBlock(LabelScopeKind.Switch);
                    // Define labels inside of the switch cases so theyare in 
                    // scope for the whole switch. This allows "goto case" and 
                    // "goto default" to be considered as local jumps.
                    var @switch = (SwitchExpression)node; 
                    foreach (SwitchCase c in @switch.Cases) {
                        DefineBlockLabels(c.Body);
                    }
                    DefineBlockLabels(@switch.DefaultBody); 
                    return true;
 
                // Remove this when Convert(Void) goes away. 
                case ExpressionType.Convert:
                    if (node.Type != typeof(void)) { 
                        // treat it as an expression
                        goto default;
                    }
                    PushLabelBlock(LabelScopeKind.Statement); 
                    return true;
 
                case ExpressionType.Conditional: 
                case ExpressionType.Loop:
                case ExpressionType.Goto: 
                    PushLabelBlock(LabelScopeKind.Statement);
                    return true;
            }
        } 

        private void DefineBlockLabels(Expression node) { 
            var block = node as BlockExpression; 
            if (block == null || block is SpilledExpressionBlock) {
                return; 
            }
            for (int i = 0, n = block.ExpressionCount; i < n; i++) {
                Expression e = block.GetExpression(i);
 
                var label = e as LabelExpression;
                if (label != null) { 
                    DefineLabel(label.Target); 
                }
            } 
        }

        // See if this lambda has a return label
        // If so, we'll create it now and mark it as allowing the "ret" opcode 
        // This allows us to generate better IL
        private void AddReturnLabel(LambdaExpression lambda) { 
            var expression = lambda.Body; 

            while (true) { 
                switch (expression.NodeType) {
                    default:
                        // Didn't find return label
                        return; 
                    case ExpressionType.Label:
                        // Found the label. We can directly return from this place 
                        // only if the label type is reference assignable to the lambda return type. 
                        var label = ((LabelExpression)expression).Target;
                        _labelInfo.Add(label, new LabelInfo(_ilg, label, TypeUtils.AreReferenceAssignable(lambda.ReturnType, label.Type))); 
                        return;
                    case ExpressionType.Block:
                        // Look in the last significant expression of a block
                        var body = (BlockExpression)expression; 
                        // omit empty and debuginfo at the end of the block since they
                        // are not going to emit any IL 
                        for (int i = body.ExpressionCount - 1; i >= 0; i--) { 
                            expression = body.GetExpression(i);
                            if (Significant(expression)) { 
                                break;
                            }
                        }
                        continue; 
                }
            } 
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// Copyright (c) Microsoft Corporation. All rights reserved.
/* **************************************************************************** 
 *
 * 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.
 *
 *
 * ***************************************************************************/ 

using System.Diagnostics; 
using System.Dynamic.Utils; 

namespace System.Linq.Expressions.Compiler { 

    // The part of the LambdaCompiler dealing with low level control flow
    // break, contiue, return, exceptions, etc
    partial class LambdaCompiler { 

        private LabelInfo EnsureLabel(LabelTarget node) { 
            LabelInfo result; 
            if (!_labelInfo.TryGetValue(node, out result)) {
                _labelInfo.Add(node, result = new LabelInfo(_ilg, node, false)); 
            }
            return result;
        }
 
        private LabelInfo ReferenceLabel(LabelTarget node) {
            LabelInfo result = EnsureLabel(node); 
            result.Reference(_labelBlock); 
            return result;
        } 

        private LabelInfo DefineLabel(LabelTarget node) {
            if (node == null) {
                return new LabelInfo(_ilg, null, false); 
            }
            LabelInfo result = EnsureLabel(node); 
            result.Define(_labelBlock); 
            return result;
        } 

        private void PushLabelBlock(LabelScopeKind type) {
            _labelBlock = new LabelScopeInfo(_labelBlock, type);
        } 

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "kind")] 
        private void PopLabelBlock(LabelScopeKind kind) { 
            Debug.Assert(_labelBlock != null && _labelBlock.Kind == kind);
            _labelBlock = _labelBlock.Parent; 
        }

        private void EmitLabelExpression(Expression expr, CompilationFlags flags) {
            var node = (LabelExpression)expr; 
            Debug.Assert(node.Target != null);
 
            // If we're an immediate child of a block, our label will already 
            // be defined. If not, we need to define our own block so this
            // label isn't exposed except to its own child expression. 
            LabelInfo label = null;

            if (_labelBlock.Kind == LabelScopeKind.Block) {
                _labelBlock.TryGetLabelInfo(node.Target, out label); 

                // We're in a block but didn't find our label, try switch 
                if (label == null && _labelBlock.Parent.Kind == LabelScopeKind.Switch) { 
                    _labelBlock.Parent.TryGetLabelInfo(node.Target, out label);
                } 

                // if we're in a switch or block, we should've found the label
                Debug.Assert(label != null);
            } 

            if (label == null) { 
                label = DefineLabel(node.Target); 
            }
 
            if (node.DefaultValue != null) {
                if (node.Target.Type == typeof(void)) {
                    EmitExpressionAsVoid(node.DefaultValue, flags);
                } else { 
                    flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart);
                    EmitExpression(node.DefaultValue, flags); 
                } 
            }
 
            label.Mark();
        }

        private void EmitGotoExpression(Expression expr, CompilationFlags flags) { 
            var node = (GotoExpression)expr;
            var labelInfo = ReferenceLabel(node.Target); 
 
            var tailCall = flags & CompilationFlags.EmitAsTailCallMask;
            if (tailCall != CompilationFlags.EmitAsNoTail) { 
                // Since tail call flags are not passed into EmitTryExpression, CanReturn
                // means the goto will be emitted as Ret. Therefore we can emit the goto's
                // default value with tail call. This can be improved by detecting if the
                // target label is equivalent to the return label. 
                tailCall = labelInfo.CanReturn ? CompilationFlags.EmitAsTail : CompilationFlags.EmitAsNoTail;
                flags = UpdateEmitAsTailCallFlag(flags, tailCall); 
            } 

            if (node.Value != null) { 
                if (node.Target.Type == typeof(void)) {
                    EmitExpressionAsVoid(node.Value, flags);
                } else {
                    flags = UpdateEmitExpressionStartFlag(flags, CompilationFlags.EmitExpressionStart); 
                    EmitExpression(node.Value, flags);
                } 
            } 

            labelInfo.EmitJump(); 

            EmitUnreachable(node, flags);
        }
 
        // We need to push default(T), unless we're emitting ourselves as
        // void. Even though the code is unreachable, we still have to 
        // generate correct IL. We can get rid of this once we have better 
        // reachability analysis.
        private void EmitUnreachable(Expression node, CompilationFlags flags) { 
            if (node.Type != typeof(void) && (flags & CompilationFlags.EmitAsVoidType) == 0) {
                _ilg.EmitDefault(node.Type);
            }
        } 

        private bool TryPushLabelBlock(Expression node) { 
            // Anything that is "statement-like" -- e.g. has no associated 
            // stack state can be jumped into, with the exception of try-blocks
            // We indicate this by a "Block" 
            //
            // Otherwise, we push an "Expression" to indicate that it can't be
            // jumped into
            switch (node.NodeType) { 
                default:
                    if (_labelBlock.Kind != LabelScopeKind.Expression) { 
                        PushLabelBlock(LabelScopeKind.Expression); 
                        return true;
                    } 
                    return false;
                case ExpressionType.Label:
                    // LabelExpression is a bit special, if it's directly in a
                    // block it becomes associate with the block's scope. Same 
                    // thing if it's in a switch case body.
                    if (_labelBlock.Kind == LabelScopeKind.Block) { 
                        var label = ((LabelExpression)node).Target; 
                        if (_labelBlock.ContainsTarget(label)) {
                            return false; 
                        }
                        if (_labelBlock.Parent.Kind == LabelScopeKind.Switch &&
                            _labelBlock.Parent.ContainsTarget(label)) {
                            return false; 
                        }
                    } 
                    PushLabelBlock(LabelScopeKind.Statement); 
                    return true;
                case ExpressionType.Block: 
                    if (node is SpilledExpressionBlock) {
                        // treat it as an expression
                        goto default;
                    } 

                    PushLabelBlock(LabelScopeKind.Block); 
                    // Labels defined immediately in the block are valid for 
                    // the whole block.
                    if (_labelBlock.Parent.Kind != LabelScopeKind.Switch) { 
                        DefineBlockLabels(node);
                    }
                    return true;
                case ExpressionType.Switch: 
                    PushLabelBlock(LabelScopeKind.Switch);
                    // Define labels inside of the switch cases so theyare in 
                    // scope for the whole switch. This allows "goto case" and 
                    // "goto default" to be considered as local jumps.
                    var @switch = (SwitchExpression)node; 
                    foreach (SwitchCase c in @switch.Cases) {
                        DefineBlockLabels(c.Body);
                    }
                    DefineBlockLabels(@switch.DefaultBody); 
                    return true;
 
                // Remove this when Convert(Void) goes away. 
                case ExpressionType.Convert:
                    if (node.Type != typeof(void)) { 
                        // treat it as an expression
                        goto default;
                    }
                    PushLabelBlock(LabelScopeKind.Statement); 
                    return true;
 
                case ExpressionType.Conditional: 
                case ExpressionType.Loop:
                case ExpressionType.Goto: 
                    PushLabelBlock(LabelScopeKind.Statement);
                    return true;
            }
        } 

        private void DefineBlockLabels(Expression node) { 
            var block = node as BlockExpression; 
            if (block == null || block is SpilledExpressionBlock) {
                return; 
            }
            for (int i = 0, n = block.ExpressionCount; i < n; i++) {
                Expression e = block.GetExpression(i);
 
                var label = e as LabelExpression;
                if (label != null) { 
                    DefineLabel(label.Target); 
                }
            } 
        }

        // See if this lambda has a return label
        // If so, we'll create it now and mark it as allowing the "ret" opcode 
        // This allows us to generate better IL
        private void AddReturnLabel(LambdaExpression lambda) { 
            var expression = lambda.Body; 

            while (true) { 
                switch (expression.NodeType) {
                    default:
                        // Didn't find return label
                        return; 
                    case ExpressionType.Label:
                        // Found the label. We can directly return from this place 
                        // only if the label type is reference assignable to the lambda return type. 
                        var label = ((LabelExpression)expression).Target;
                        _labelInfo.Add(label, new LabelInfo(_ilg, label, TypeUtils.AreReferenceAssignable(lambda.ReturnType, label.Type))); 
                        return;
                    case ExpressionType.Block:
                        // Look in the last significant expression of a block
                        var body = (BlockExpression)expression; 
                        // omit empty and debuginfo at the end of the block since they
                        // are not going to emit any IL 
                        for (int i = body.ExpressionCount - 1; i >= 0; i--) { 
                            expression = body.GetExpression(i);
                            if (Significant(expression)) { 
                                break;
                            }
                        }
                        continue; 
                }
            } 
        } 
    }
} 

// 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