Permalink
Switch branches/tags
version-2.9.0 version-2.8.2 version-2.8.0 version-2.7.0-beta3 version-2.6.0-beta3 version-2.4.0 version-2.3.5 version-2.3.4 version-2.3.2 version-2.3.2-beta1 version-2.3.0-beta3 version-2.3.0-beta2 version-2.3.0-beta1 version-2.2.0 version-2.1.0 version-2.0.0 version-2.0.0-rc4 version-2.0.0-rc3 version-2.0.0-rc2 version-2.0.0-rc version-2.0.0-beta5 version-2.0.0-beta4 version-2.0.0-beta3 version-2.0.0-beta1 version-1.3.2 version-1.3.1 version-1.3.0 version-1.3.0-beta1-20160429-01 version-1.2.2 version-1.2.1 version-1.2.0 version-1.2.0-beta1-20160108-01 version-1.2.0-beta version-1.2.0-beta-20151211-01 version-1.1.1 version-1.1.0 version-1.1.0-rc1-20151109-01 version-1.0.0 version-1.0.0-beta1-20141031-01 toolset_5 toolset_3 toolset_2 toolset_1_1 toolset_1 Visual.Studio.2015.Update.1.RC Visual.Studio.2015.Update.1.CTP Visual-Studio-2017 Visual-Studio-2017-Version-15.8 Visual-Studio-2017-Version-15.7.2 Visual-Studio-2017-Version-15.7 Visual-Studio-2017-Version-15.6 Visual-Studio-2017-Version-15.5 Visual-Studio-2017-Version-15.4 Visual-Studio-2017-Version-15.3.5 Visual-Studio-2017-Version-15.3.4 Visual-Studio-2017-Version-15.3.2 Visual-Studio-2017-Version-15.3 Visual-Studio-2017-Version-15.2 Visual-Studio-2017-Version-15.1 Visual-Studio-2017-RC4 Visual-Studio-2017-RC3 Visual-Studio-2017-RC2 Visual-Studio-2017-RC Visual-Studio-2017-Preview-Version-15.3 Visual-Studio-2017-Preview-6-Version-15.7 Visual-Studio-2017-Preview-3-Version-15.4 Visual-Studio-2017-Preview-3-Version-15.3 Visual-Studio-2017-Preview-2-Version-15.4 Visual-Studio-2017-Preview-2-Version-15.3 Visual-Studio-2017-Preview-1-Version-15.4 Visual-Studio-2015 Visual-Studio-2015-Update-3 Visual-Studio-2015-Update-3-Micro-Update-1 Visual-Studio-2015-Update-2 Visual-Studio-2015-Update-2-RC Visual-Studio-2015-Update-2-Micro-Update-3 Visual-Studio-2015-Update-2-Micro-Update-1 Visual-Studio-2015-Update-1 Visual-Studio-2015-Update-1-RC Visual-Studio-2015-Update-1-CTP Visual-Studio-2015-RC Visual-Studio-2015-Preview Visual-Studio-2015-CTP-6 Visual-Studio-2015-CTP-5 Visual-Studio-15-Preview Visual-Studio-15-Preview-5 Visual-Studio-15-Preview-4 Visual-Studio-15-Preview-3 VS.Toolset.Roslyn.1.1.0-beta1-20150727-01 VS.Tools.X86.Managed.V45.1.0.150513.2 Oss.Scan.2015.03.13 Oss.Scan.2013.03.13 NetFx.Toolset.150729
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
301 lines (256 sloc) 13.6 KB
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Immutable;
using System.Diagnostics;
using Microsoft.CodeAnalysis.CodeGen;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CSharp
{
internal partial class LocalRewriter
{
// Rewriting for integral and string switch statements.
//
// For switch statements, we have an option of completely rewriting the switch header
// and switch sections into simpler constructs, i.e. we can rewrite the switch header
// using bound conditional goto statements and the rewrite the switch sections into
// bound labeled statements.
// However, all the logic for emitting the switch jump tables is language agnostic
// and includes IL optimizations. Hence we delay the switch jump table generation
// till the emit phase. This way we also get additional benefit of sharing this code
// between both VB and C# compilers.
// For integral switch statements, we delay almost all the work
// to the emit phase.
// For string switch statements, we need to determine if we are generating a hash
// table based jump table or a non hash jump table, i.e. linear string comparisons
// with each case label. We use the Dev10 Heuristic to determine this
// (see SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch() for details).
// If we are generating a hash table based jump table, we use a simple customizable
// hash function to hash the string constants corresponding to the case labels.
// See SwitchStringJumpTableEmitter.ComputeStringHash().
// We need to emit this function to compute the hash value into the compiler generate
// <PrivateImplementationDetails> class.
// If we have at least one string switch statement in a module that needs a
// hash table based jump table, we generate a single public string hash synthesized method
// that is shared across the module.
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node)
{
var syntax = node.Syntax;
var rewrittenExpression = (BoundExpression)Visit(node.Expression);
var rewrittenSections = VisitSwitchSections(node.SwitchSections);
// EnC: We need to insert a hidden sequence point to handle function remapping in case
// the containing method is edited while methods invoked in the expression are being executed.
if (!node.WasCompilerGenerated && this.Instrument)
{
rewrittenExpression = _instrumenter.InstrumentSwitchStatementExpression(node, rewrittenExpression, _factory);
}
var rewrittenStatement = MakeSwitchStatement(syntax, rewrittenExpression, rewrittenSections, node.ConstantTargetOpt, node.InnerLocals, node.InnerLocalFunctions, node.BreakLabel, node);
// Only add instrumentation (such as a sequence point) if the node is not compiler-generated.
if (this.Instrument && !node.WasCompilerGenerated)
{
rewrittenStatement = _instrumenter.InstrumentSwitchStatement(node, rewrittenStatement);
}
return rewrittenStatement;
}
private BoundStatement MakeSwitchStatement(
SyntaxNode syntax,
BoundExpression rewrittenExpression,
ImmutableArray<BoundSwitchSection> rewrittenSections,
LabelSymbol constantTargetOpt,
ImmutableArray<LocalSymbol> locals,
ImmutableArray<LocalFunctionSymbol> localFunctions,
GeneratedLabelSymbol breakLabel,
BoundSwitchStatement oldNode)
{
Debug.Assert(oldNode != null);
Debug.Assert((object)rewrittenExpression.Type != null);
return rewrittenExpression.Type.IsNullableType() ?
MakeSwitchStatementWithNullableExpression(syntax, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, localFunctions, breakLabel, oldNode) :
MakeSwitchStatementWithNonNullableExpression(syntax, null, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, localFunctions, breakLabel, oldNode);
}
private BoundStatement MakeSwitchStatementWithNonNullableExpression(
SyntaxNode syntax,
BoundStatement preambleOpt,
BoundExpression rewrittenExpression,
ImmutableArray<BoundSwitchSection> rewrittenSections,
LabelSymbol constantTargetOpt,
ImmutableArray<LocalSymbol> locals,
ImmutableArray<LocalFunctionSymbol> localFunctions,
GeneratedLabelSymbol breakLabel,
BoundSwitchStatement oldNode)
{
Debug.Assert(!rewrittenExpression.Type.IsNullableType());
Debug.Assert((object)oldNode.StringEquality == null);
// If we are emitting a hash table based string switch,
// we need to generate a helper method for computing
// string hash value in <PrivateImplementationDetails> class.
MethodSymbol stringEquality = null;
if (rewrittenExpression.Type.SpecialType == SpecialType.System_String)
{
EnsureStringHashFunction(rewrittenSections, syntax);
stringEquality = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__op_Equality);
}
return oldNode.Update(
loweredPreambleOpt: preambleOpt,
expression: rewrittenExpression,
constantTargetOpt: constantTargetOpt,
innerLocals: locals,
innerLocalFunctions: localFunctions,
switchSections: rewrittenSections,
breakLabel: breakLabel,
stringEquality: stringEquality);
}
private BoundStatement MakeSwitchStatementWithNullableExpression(
SyntaxNode syntax,
BoundExpression rewrittenExpression,
ImmutableArray<BoundSwitchSection> rewrittenSections,
LabelSymbol constantTargetOpt,
ImmutableArray<LocalSymbol> locals,
ImmutableArray<LocalFunctionSymbol> localFunctions,
GeneratedLabelSymbol breakLabel,
BoundSwitchStatement oldNode)
{
Debug.Assert(rewrittenExpression.Type.IsNullableType());
var exprSyntax = rewrittenExpression.Syntax;
var exprNullableType = rewrittenExpression.Type;
var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
// Rewrite the nullable expression to a temp as we might have a user defined conversion from source expression to switch governing type.
// We can avoid generating the temp if the expression is a bound local.
LocalSymbol tempLocal;
if (rewrittenExpression.Kind != BoundKind.Local)
{
BoundAssignmentOperator assignmentToTemp;
BoundLocal boundTemp = _factory.StoreToTemp(rewrittenExpression, out assignmentToTemp);
var tempAssignment = new BoundExpressionStatement(exprSyntax, assignmentToTemp);
statementBuilder.Add(tempAssignment);
tempLocal = boundTemp.LocalSymbol;
rewrittenExpression = boundTemp;
}
else
{
tempLocal = null;
}
// Generate a BoundConditionalGoto with null check as the conditional expression and appropriate switch label as the target: null, default or exit label.
BoundStatement condGotoNullValueTargetLabel = new BoundConditionalGoto(
exprSyntax,
condition: MakeNullCheck(exprSyntax, rewrittenExpression, BinaryOperatorKind.NullableNullEqual),
jumpIfTrue: true,
label: GetNullValueTargetSwitchLabel(rewrittenSections, breakLabel));
// Rewrite the switch statement using nullable expression's underlying value as the switch expression.
// rewrittenExpression.GetValueOrDefault()
MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(syntax, exprNullableType, SpecialMember.System_Nullable_T_GetValueOrDefault);
BoundCall callGetValueOrDefault = BoundCall.Synthesized(exprSyntax, rewrittenExpression, getValueOrDefault);
rewrittenExpression = callGetValueOrDefault;
// rewrite switch statement
BoundStatement rewrittenSwitchStatement = MakeSwitchStatementWithNonNullableExpression(
syntax,
condGotoNullValueTargetLabel,
rewrittenExpression,
rewrittenSections,
constantTargetOpt,
locals,
localFunctions,
breakLabel,
oldNode);
statementBuilder.Add(rewrittenSwitchStatement);
return new BoundBlock(
syntax,
locals: (object)tempLocal == null ? ImmutableArray<LocalSymbol>.Empty : ImmutableArray.Create<LocalSymbol>(tempLocal),
statements: statementBuilder.ToImmutableAndFree());
}
private static LabelSymbol GetNullValueTargetSwitchLabel(ImmutableArray<BoundSwitchSection> sections, GeneratedLabelSymbol breakLabel)
{
LabelSymbol fallThroughLabel = breakLabel;
foreach (var section in sections)
{
foreach (BoundSwitchLabel boundLabel in section.SwitchLabels)
{
var label = (SourceLabelSymbol)boundLabel.Label;
var labelConstant = boundLabel.ConstantValueOpt;
if (labelConstant == ConstantValue.Null)
{
return label;
}
else if (labelConstant == null)
{
// Default label
Debug.Assert(label.IdentifierNodeOrToken.Kind() == SyntaxKind.DefaultSwitchLabel);
Debug.Assert(fallThroughLabel == breakLabel);
fallThroughLabel = label;
}
}
}
return fallThroughLabel;
}
private ImmutableArray<BoundSwitchSection> VisitSwitchSections(ImmutableArray<BoundSwitchSection> sections)
{
if (sections.Length > 0)
{
// Visit the switch sections
var sectionsBuilder = ArrayBuilder<BoundSwitchSection>.GetInstance();
foreach (BoundSwitchSection section in sections)
{
sectionsBuilder.Add((BoundSwitchSection)VisitSwitchSection(section));
}
return sectionsBuilder.ToImmutableAndFree();
}
return ImmutableArray<BoundSwitchSection>.Empty;
}
public override BoundNode VisitSwitchSection(BoundSwitchSection node)
{
Debug.Assert(node.Locals.IsEmpty);
return node.Update(locals: node.Locals, VisitList(node.SwitchLabels), VisitList(node.Statements));
}
private static int CountLabels(ImmutableArray<BoundSwitchSection> rewrittenSections)
{
int count = 0;
foreach (var section in rewrittenSections)
{
foreach (var boundLabel in section.SwitchLabels)
{
if (boundLabel.ConstantValueOpt != null)
{
var value = boundLabel.ConstantValueOpt;
Debug.Assert(value.IsString || value.IsNull);
count++;
}
}
}
return count;
}
// Checks whether we are generating a hash table based string switch and
// we need to generate a new helper method for computing string hash value.
// Creates the method if needed.
private void EnsureStringHashFunction(ImmutableArray<BoundSwitchSection> rewrittenSections, SyntaxNode syntaxNode)
{
var module = this.EmitModule;
if (module == null)
{
return;
}
int labelsCount = CountLabels(rewrittenSections);
if (!SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch(module, labelsCount))
{
return;
}
// If we have already generated the helper, possibly for another switch
// or on another thread, we don't need to regenerate it.
var privateImplClass = module.GetPrivateImplClass(syntaxNode, _diagnostics);
if (privateImplClass.GetMethod(PrivateImplementationDetails.SynthesizedStringHashFunctionName) != null)
{
return;
}
// cannot emit hash method if have no access to Chars.
var charsMember = _compilation.GetSpecialTypeMember(SpecialMember.System_String__Chars);
if ((object)charsMember == null || charsMember.GetUseSiteDiagnostic() != null)
{
return;
}
TypeSymbol returnType = _factory.SpecialType(SpecialType.System_UInt32);
TypeSymbol paramType = _factory.SpecialType(SpecialType.System_String);
var method = new SynthesizedStringSwitchHashMethod(module.SourceModule, privateImplClass, returnType, paramType);
privateImplClass.TryAddSynthesizedMethod(method);
}
}
}