コード例 #1
0
        private static SwitchAnalysis Analyze(IList <CSharpSwitchCase> cases)
        {
            var res = new SwitchAnalysis
            {
                OtherCases = new List <CSharpSwitchCase>()
            };

            var n = cases.Count;

            for (var i = 0; i < n; i++)
            {
                var @case = cases[i];

                var foundNull    = false;
                var foundDefault = false;

                foreach (var testValue in @case.TestValues)
                {
                    if (testValue == null)
                    {
                        foundNull = true;
                    }
                    else if (testValue == SwitchCaseDefaultValue)
                    {
                        foundDefault = true;
                    }
                }

                // NB: We could optimize the case where there are other test values besides null in the null-case. However,
                //     we'd have to hoist the case body out of the switch while still having switch cases for the non-null
                //     test values to branch into the hoisted body. This is doable, but it seems rare to have null alongside
                //     non-null test values in a switch case. Also, the reduced form would become more clunky for the reader
                //     although we have other precedents a la async lambdas of course :-).

                var lonely = @case.TestValues.Count == 1;

                if (foundNull)
                {
                    res.NullCase     = @case;
                    res.IsNullLonely = lonely;
                }

                if (foundDefault)
                {
                    res.DefaultCase     = @case;
                    res.IsDefaultLonely = lonely;
                }

                if (!foundNull && !foundDefault)
                {
                    res.OtherCases.Add(@case);
                }
            }

            return(res);
        }
コード例 #2
0
        private static LoweredSwitchStatement LowerSwitchStatement(SwitchAnalysis analysis, Type testValueType, bool hoistNull = false)
        {
            var res = new LoweredSwitchStatement();

            var defaultCase = analysis.DefaultCase;

            if (defaultCase != null)
            {
                Debug.Assert(analysis.IsDefaultLonely);
                Debug.Assert(defaultCase.TestValues.Count == 1);

                var body = MakeBlock(defaultCase.Statements);
                res.DefaultCase = body;
            }

            var otherCases = analysis.OtherCases;
            var n          = otherCases.Count;

            var cases = new List <SwitchCase>(n);

            for (var i = 0; i < n; i++)
            {
                var @case = ConvertSwitchCase(otherCases[i], testValueType);
                cases.Add(@case);
            }

            var nullCase = analysis.NullCase;

            if (nullCase != null)
            {
                if (hoistNull)
                {
                    Debug.Assert(analysis.IsNullLonely);
                    Debug.Assert(nullCase.TestValues.Count == 1);

                    var body = MakeBlock(nullCase.Statements);
                    res.NullCase = body;
                }
                else
                {
                    var @case = ConvertSwitchCase(nullCase, testValueType);
                    cases.Add(@case);
                }
            }

            res.Cases = cases;

            return(res);
        }
コード例 #3
0
        private Expression ReduceNonNullable(SwitchAnalysis analysis)
        {
            var lowered = LowerSwitchStatement(analysis, SwitchValue.Type, hoistNull: false);

            Expression res;

            if (Variables.Count > 0)
            {
                // NB: Variable scope should not include the switch value.

                var value = Expression.Parameter(SwitchValue.Type);

                var @switch = lowered.Make(value, lowered.DefaultCase);

                var exprs = new Expression[]
                {
                    Expression.Assign(value, SwitchValue),
                    WithVariableScope(@switch),
                    Expression.Label(BreakLabel),
                };

                res = Expression.Block(new TrueReadOnlyCollection <ParameterExpression>(new[] { value }), new TrueReadOnlyCollection <Expression>(exprs));
            }
            else
            {
                var @switch = lowered.Make(SwitchValue, lowered.DefaultCase);

                var exprs = new Expression[]
                {
                    @switch,
                    Expression.Label(BreakLabel),
                };

                res = Expression.Block(new TrueReadOnlyCollection <Expression>(exprs));
            }

            return(res);
        }
コード例 #4
0
        private Expression ReduceNullable(SwitchAnalysis analysis)
        {
            var governingType        = SwitchValue.Type;
            var governingTypeNonNull = governingType.GetNonNullableType();

            var valueLocal        = Expression.Parameter(governingType, "__value");
            var vars              = new[] { valueLocal };
            var assignSwitchValue = Expression.Assign(valueLocal, SwitchValue);

            Expression body;

            // NB: The DLR switch expression does not optimize the nullable case into a switch table after a null check.
            //     We can do this here instead, but we could consider moving this logic to the DLR too.

            if (analysis.NullCase != null)
            {
                if (analysis.IsNullLonely)
                {
                    // We found a case with only a 'null' test value; we can lower to a null-check followed by a non-null switch,
                    // and move the 'null' case to an else branch.

                    var hasValue = MakeNullableHasValue(valueLocal);
                    var value    = MakeNullableGetValueOrDefault(valueLocal);

                    var lowered = LowerSwitchStatement(analysis, governingTypeNonNull, hoistNull: true);

                    var @switch = lowered.Make(value, lowered.DefaultCase);

                    body = Expression.IfThenElse(hasValue, @switch, lowered.NullCase);
                }
                else
                {
                    // We have a case with a 'null' test value but it has other non-null test values too; we can't lower to non-
                    // null types. This should be the rare case. The DLR will further lower to if-then-else branches.

                    var lowered = LowerSwitchStatement(analysis, governingType, hoistNull: false);

                    body = lowered.Make(valueLocal, lowered.DefaultCase);
                }
            }
            else
            {
                // We have no 'null' test value whatsoever; we can lower to a null-check followed by a non-null switch.

                var hasValue = MakeNullableHasValue(valueLocal);
                var value    = MakeNullableGetValueOrDefault(valueLocal);

                var lowered = LowerSwitchStatement(analysis, governingTypeNonNull, hoistNull: false);

                var defaultBody = lowered.DefaultCase;

                if (defaultBody != null)
                {
                    var defaultLabel = Expression.Label("__default");

                    var @switch = lowered.Make(value, Expression.Goto(defaultLabel));

                    var defaultCase = Expression.Block(Expression.Label(defaultLabel), defaultBody);

                    body = Expression.IfThenElse(hasValue, @switch, defaultCase);
                }
                else
                {
                    var @switch = lowered.Make(value, null);

                    body = Expression.IfThen(hasValue, @switch);
                }
            }

            // NB: Variable scope should not include the switch value.

            var exprs = new Expression[]
            {
                assignSwitchValue,
                WithVariableScope(body),
                Expression.Label(BreakLabel),
            };

            return(Expression.Block(new TrueReadOnlyCollection <ParameterExpression>(vars), new TrueReadOnlyCollection <Expression>(exprs)));
        }
コード例 #5
0
        private static LoweredSwitchStatement LowerSwitchStatement(SwitchAnalysis analysis, Type testValueType, bool hoistNull = false)
        {
            var res = new LoweredSwitchStatement();

            var defaultCase = analysis.DefaultCase;

            if (defaultCase != null)
            {
                Debug.Assert(analysis.IsDefaultLonely);
                Debug.Assert(defaultCase.TestValues.Count == 1);

                var body = MakeBlock(defaultCase.Statements);
                res.DefaultCase = body;
            }

            var otherCases = analysis.OtherCases;
            var n = otherCases.Count;

            var cases = new List<SwitchCase>(n);
            for (var i = 0; i < n; i++)
            {
                var @case = ConvertSwitchCase(otherCases[i], testValueType);
                cases.Add(@case);
            }

            var nullCase = analysis.NullCase;

            if (nullCase != null)
            {
                if (hoistNull)
                {
                    Debug.Assert(analysis.IsNullLonely);
                    Debug.Assert(nullCase.TestValues.Count == 1);

                    var body = MakeBlock(nullCase.Statements);
                    res.NullCase = body;
                }
                else
                {
                    var @case = ConvertSwitchCase(nullCase, testValueType);
                    cases.Add(@case);
                }
            }

            res.Cases = cases;

            return res;
        }
コード例 #6
0
        private Expression ReduceNullable(SwitchAnalysis analysis)
        {
            var governingType = SwitchValue.Type;
            var governingTypeNonNull = governingType.GetNonNullableType();

            var valueLocal = Expression.Parameter(governingType, "__value");
            var vars = new[] { valueLocal };
            var assignSwitchValue = Expression.Assign(valueLocal, SwitchValue);
            var body = default(Expression);

            // NB: The DLR switch expression does not optimize the nullable case into a switch table after a null check.
            //     We can do this here instead, but we could consider moving this logic to the DLR too.

            if (analysis.NullCase != null)
            {
                if (analysis.IsNullLonely)
                {
                    // We found a case with only a 'null' test value; we can lower to a null-check followed by a non-null switch,
                    // and move the 'null' case to an else branch.

                    var hasValue = Expression.Property(valueLocal, "HasValue");
                    var value = Expression.Property(valueLocal, "Value");

                    var lowered = LowerSwitchStatement(analysis, governingTypeNonNull, hoistNull: true);

                    var @switch = lowered.Make(value, lowered.DefaultCase);

                    body = Expression.IfThenElse(hasValue, @switch, lowered.NullCase);
                }
                else
                {
                    // We have a case with a 'null' test value but it has other non-null test values too; we can't lower to non-
                    // null types. This should be the rare case. The DLR will further lower to if-then-else branches.

                    var lowered = LowerSwitchStatement(analysis, governingType, hoistNull: false);

                    body = lowered.Make(valueLocal, lowered.DefaultCase);
                }
            }
            else
            {
                // We have no 'null' test value whatsoever; we can lower to a null-check followed by a non-null switch.

                var hasValue = Expression.Property(valueLocal, "HasValue");
                var value = Expression.Property(valueLocal, "Value");

                var lowered = LowerSwitchStatement(analysis, governingTypeNonNull, hoistNull: false);

                var defaultBody = lowered.DefaultCase;

                if (defaultBody != null)
                {
                    var defaultLabel = Expression.Label("__default");

                    var @switch = lowered.Make(value, Expression.Goto(defaultLabel));

                    var defaultCase = Expression.Block(Expression.Label(defaultLabel), defaultBody);

                    body = Expression.IfThenElse(hasValue, @switch, defaultCase);
                }
                else
                {
                    var @switch = lowered.Make(value, null);

                    body = Expression.IfThen(hasValue, @switch);
                }
            }

            // NB: Variable scope should not include the switch value.

            var exprs = new Expression[]
            {
                assignSwitchValue,
                WithVariableScope(body),
                Expression.Label(BreakLabel),
            };

            return Expression.Block(new TrueReadOnlyCollection<ParameterExpression>(vars), new TrueReadOnlyCollection<Expression>(exprs));
        }
コード例 #7
0
        private Expression ReduceNonNullable(SwitchAnalysis analysis)
        {
            var res = default(Expression);

            var lowered = LowerSwitchStatement(analysis, SwitchValue.Type, hoistNull: false);

            if (Variables.Count > 0)
            {
                // NB: Variable scope should not include the switch value.

                var value = Expression.Parameter(SwitchValue.Type);

                var @switch = lowered.Make(value, lowered.DefaultCase);

                var exprs = new Expression[]
                {
                    Expression.Assign(value, SwitchValue),
                    WithVariableScope(@switch),
                    Expression.Label(BreakLabel),
                };

                res = Expression.Block(new TrueReadOnlyCollection<ParameterExpression>(new[] { value }), new TrueReadOnlyCollection<Expression>(exprs));
            }
            else
            {
                var @switch = lowered.Make(SwitchValue, lowered.DefaultCase);

                var exprs = new Expression[]
                {
                    @switch,
                    Expression.Label(BreakLabel),
                };

                res = Expression.Block(new TrueReadOnlyCollection<Expression>(exprs));
            }

            return res;
        }
コード例 #8
0
        private static SwitchAnalysis Analyze(IList<CSharpSwitchCase> cases)
        {
            var res = new SwitchAnalysis();

            res.OtherCases = new List<CSharpSwitchCase>();

            var n = cases.Count;
            for (var i = 0; i < n; i++)
            {
                var @case = cases[i];

                var foundNull = false;
                var foundDefault = false;

                foreach (var testValue in @case.TestValues)
                {
                    if (testValue == null)
                    {
                        foundNull = true;
                    }
                    else if (testValue == SwitchCaseDefaultValue)
                    {
                        foundDefault = true;
                    }
                }

                // NB: We could optimize the case where there are other test values besides null in the null-case. However,
                //     we'd have to hoist the case body out of the switch while still having switch cases for the non-null
                //     test values to branch into the hoisted body. This is doable, but it seems rare to have null alongside
                //     non-null test values in a switch case. Also, the reduced form would become more clunky for the reader
                //     although we have other precedents a la async lambdas of course :-).

                var lonely = @case.TestValues.Count == 1;

                if (foundNull)
                {
                    res.NullCase = @case;
                    res.IsNullLonely = lonely;
                }

                if (foundDefault)
                {
                    res.DefaultCase = @case;
                    res.IsDefaultLonely = lonely;
                }

                if (!foundNull && !foundDefault)
                {
                    res.OtherCases.Add(@case);
                }
            }

            return res;
        }