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); }
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); }
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); }
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))); }
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; }
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)); }
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; }
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; }