private BoundStatement MakeSwitchStatementWithNonNullableExpression( CSharpSyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray <LocalSymbol> locals, 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 = GetSpecialTypeMethod(syntax, SpecialMember.System_String__op_Equality); } return(oldNode.Update( boundExpression: rewrittenExpression, constantTargetOpt: constantTargetOpt, innerLocals: locals, switchSections: rewrittenSections, breakLabel: breakLabel, stringEquality: stringEquality)); }
public override BoundStatement InstrumentSwitchStatement( BoundSwitchStatement original, BoundStatement rewritten ) { return(Previous.InstrumentSwitchStatement(original, rewritten)); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { AddAll(node.InnerLocals); base.VisitSwitchStatement(node); RemoveAll(node.InnerLocals); return(null); }
// 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); // Create the sequence point if generating debug info and // node is not compiler generated if (this.Instrument && !node.WasCompilerGenerated) { rewrittenStatement = _instrumenter.InstrumentSwitchStatement(node, rewrittenStatement); } return rewrittenStatement; }
// 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. var rewrittenStatement = MakeSwitchStatement(syntax, AddConditionSequencePoint(rewrittenExpression, node), rewrittenSections, node.ConstantTargetOpt, node.InnerLocals, node.InnerLocalFunctions, node.BreakLabel, node); // Create the sequence point if generating debug info and // node is not compiler generated if (this.GenerateDebugInfo && !node.WasCompilerGenerated) { SwitchStatementSyntax switchSyntax = (SwitchStatementSyntax)syntax; TextSpan switchSequencePointSpan = TextSpan.FromBounds( switchSyntax.SwitchKeyword.SpanStart, switchSyntax.CloseParenToken.Span.End); return(new BoundSequencePointWithSpan( syntax: syntax, statementOpt: rewrittenStatement, span: switchSequencePointSpan, hasErrors: false)); } return(rewrittenStatement); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { // dispatch to the switch sections var initialState = VisitSwitchStatementDispatch(node); // visit switch sections var afterSwitchState = UnreachableState(); var switchSections = node.SwitchSections; var iLastSection = (switchSections.Length - 1); for (var iSection = 0; iSection <= iLastSection; iSection++) { VisitSwitchSection(switchSections[iSection], iSection == iLastSection); // Even though it is illegal for the end of a switch section to be reachable, in erroneous // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState). Join(ref afterSwitchState, ref this.State); } if (node.DecisionDag.ReachableLabels.Contains(node.BreakLabel) || (node.DefaultLabel == null && node.Expression.ConstantValue == null && IsTraditionalSwitch(node))) { Join(ref afterSwitchState, ref initialState); } ResolveBreaks(afterSwitchState, node.BreakLabel); return(null); }
// 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.BoundExpression); 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. var rewrittenStatement = MakeSwitchStatement(syntax, AddConditionSequencePoint(rewrittenExpression, node), rewrittenSections, node.ConstantTargetOpt, node.InnerLocals, node.BreakLabel, node); // Create the sequence point if generating debug info and // node is not compiler generated if (this.GenerateDebugInfo && !node.WasCompilerGenerated) { SwitchStatementSyntax switchSyntax = (SwitchStatementSyntax)syntax; TextSpan switchSequencePointSpan = TextSpan.FromBounds( switchSyntax.SwitchKeyword.SpanStart, switchSyntax.CloseParenToken.Span.End); return new BoundSequencePointWithSpan( syntax: syntax, statementOpt: rewrittenStatement, span: switchSequencePointSpan, hasErrors: false); } return rewrittenStatement; }
/// <summary> /// Is the switch statement one that could be interpreted as a C# 6 or earlier switch statement? /// </summary> private bool IsTraditionalSwitch(BoundSwitchStatement node) { // Before recursive patterns were introduced, we did not consider handling both 'true' and 'false' to // completely handle all case of a switch on a bool unless there was some patterny syntax or semantics // in the switch. We had two different bound nodes and separate flow analysis handling for // "traditional" switch statements and "pattern-based" switch statements. We simulate that behavior // by testing to see if this switch would have been handled under the old rules by the old compiler. // If we are in a recent enough language version, we treat the switch as a fully pattern-based switch // for the purposes of flow analysis. if (compilation.LanguageVersion >= MessageID.IDS_FeatureRecursivePatterns.RequiredVersion()) { return(false); } if (!node.Expression.Type.IsValidV6SwitchGoverningType()) { return(false); } foreach (var sectionSyntax in ((SwitchStatementSyntax)node.Syntax).Sections) { foreach (var label in sectionSyntax.Labels) { if (label.Kind() == SyntaxKind.CasePatternSwitchLabel) { return(false); } } } return(true); }
protected override LocalState VisitSwitchStatementDispatch(BoundSwitchStatement node) { // first, learn from any null tests in the patterns int slot = MakeSlot(node.Expression); if (slot > 0) { var originalInputType = node.Expression.Type; foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { LearnFromAnyNullPatterns(slot, originalInputType, label.Pattern); } } } // visit switch header var expressionState = VisitRvalueWithState(node.Expression); LocalState initialState = this.State.Clone(); var labelStateMap = LearnFromDecisionDag(node.Syntax, node.DecisionDag, node.Expression, expressionState, ref initialState); foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { var labelResult = labelStateMap.TryGetValue(label.Label, out var s1) ? s1 : (state : UnreachableState(), believedReachable : false); SetState(labelResult.state); PendingBranches.Add(new PendingBranch(label, this.State, label.Label)); } } labelStateMap.Free(); return(initialState); }
public virtual BoundExpression InstrumentSwitchStatementExpression(BoundSwitchStatement original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory) { Debug.Assert(!original.WasCompilerGenerated); Debug.Assert(original.Syntax.Kind() == SyntaxKind.SwitchStatement); Debug.Assert(factory != null); return(rewrittenExpression); }
// 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 labeleled 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 sythesized method // that is shared across the module. public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { var syntax = node.Syntax; var rewrittenExpression = (BoundExpression)Visit(node.BoundExpression); var rewrittenSections = VisitSwitchSections(node.SwitchSections); var rewrittenStatement = MakeSwitchStatement(syntax, AddConditionSequencePoint(rewrittenExpression, node), rewrittenSections, node.ConstantTargetOpt, node.InnerLocals, node.BreakLabel, node); if (!node.OuterLocals.IsEmpty) { rewrittenStatement = new BoundBlock(syntax, node.OuterLocals, ImmutableArray.Create(rewrittenStatement)); } // Create the sequence point if generating debug info and // node is not compiler generated if (this.generateDebugInfo && !node.WasCompilerGenerated) { SwitchStatementSyntax switchSyntax = (SwitchStatementSyntax)syntax; TextSpan switchSequencePointSpan = TextSpan.FromBounds( switchSyntax.SwitchKeyword.SpanStart, switchSyntax.CloseParenToken.Span.End); return new BoundSequencePointWithSpan( syntax: syntax, statementOpt: rewrittenStatement, span: switchSequencePointSpan, hasErrors: false); } return rewrittenStatement; }
// 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); // Create the sequence point if generating debug info and // node is not compiler generated if (this.Instrument && !node.WasCompilerGenerated) { rewrittenStatement = _instrumenter.InstrumentSwitchStatement(node, rewrittenStatement); } return(rewrittenStatement); }
// 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 labeleled 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 sythesized method // that is shared across the module. public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { var syntax = node.Syntax; var rewrittenExpression = (BoundExpression)Visit(node.BoundExpression); var rewrittenSections = VisitSwitchSections(node.SwitchSections); var rewrittenStatement = MakeSwitchStatement(syntax, AddConditionSequencePoint(rewrittenExpression, node), rewrittenSections, node.ConstantTargetOpt, node.InnerLocals, node.BreakLabel, node); // Create the sequence point if generating debug info and // node is not compiler generated if (this.GenerateDebugInfo && !node.WasCompilerGenerated) { SwitchStatementSyntax switchSyntax = (SwitchStatementSyntax)syntax; TextSpan switchSequencePointSpan = TextSpan.FromBounds( switchSyntax.SwitchKeyword.SpanStart, switchSyntax.CloseParenToken.Span.End); return(new BoundSequencePointWithSpan( syntax: syntax, statementOpt: rewrittenStatement, span: switchSequencePointSpan, hasErrors: false)); } return(rewrittenStatement); }
private BoundStatement MakeSwitchStatementWithNullableExpression( CSharpSyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray <LocalSymbol> locals, 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 = this.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)); statementBuilder.Add(condGotoNullValueTargetLabel); // Rewrite the switch statement using nullable expression's underlying value as the switch expression. // rewrittenExpression.GetValueOrDefault() MethodSymbol getValueOrDefault = GetNullableMethod(syntax, exprNullableType, SpecialMember.System_Nullable_T_GetValueOrDefault); BoundCall callGetValueOrDefault = BoundCall.Synthesized(exprSyntax, rewrittenExpression, getValueOrDefault); rewrittenExpression = callGetValueOrDefault; // rewrite switch statement BoundStatement rewrittenSwitchStatement = MakeSwitchStatementWithNonNullableExpression(syntax, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, breakLabel, oldNode); statementBuilder.Add(rewrittenSwitchStatement); return(new BoundBlock(syntax, locals: (object)tempLocal == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(tempLocal), statements: statementBuilder.ToImmutableAndFree())); }
public virtual BoundStatement InstrumentSwitchStatement( BoundSwitchStatement original, BoundStatement rewritten ) { Debug.Assert(original.Syntax.Kind() == SyntaxKind.SwitchStatement); return(InstrumentStatement(original, rewritten)); }
public static BoundStatement Rewrite(LocalRewriter localRewriter, BoundSwitchStatement node) { var rewriter = new SwitchStatementLocalRewriter(node, localRewriter); BoundStatement result = rewriter.LowerSwitchStatement(node); rewriter.Free(); return(result); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { BoundSpillSequence2 ss = null; BoundExpression boundExpression = VisitExpression(ref ss, node.BoundExpression); ImmutableArray <BoundSwitchSection> switchSections = (ImmutableArray <BoundSwitchSection>) this.VisitList(node.SwitchSections); return(UpdateStatement(ss, node.Update(node.OuterLocals, boundExpression, node.ConstantTargetOpt, node.InnerLocals, switchSections, node.BreakLabel, node.StringEquality))); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { if (IsInside) { _labelsInside.Add(node.BreakLabel); } return(base.VisitSwitchStatement(node)); }
protected virtual TLocalState VisitSwitchStatementDispatch(BoundSwitchStatement node) { // visit switch header VisitRvalue(node.Expression); TLocalState initialState = this.State.Clone(); var reachableLabels = node.DecisionDag.ReachableLabels; foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { if ( reachableLabels.Contains(label.Label) || label.HasErrors || label == node.DefaultLabel && node.Expression.ConstantValue == null && IsTraditionalSwitch(node) ) { SetState(initialState.Clone()); } else { SetUnreachable(); } VisitPattern(label.Pattern); SetState(StateWhenTrue); if (label.WhenClause != null) { VisitCondition(label.WhenClause); SetState(StateWhenTrue); } PendingBranches.Add(new PendingBranch(label, this.State, label.Label)); } } TLocalState afterSwitchState = UnreachableState(); if ( node.DecisionDag.ReachableLabels.Contains(node.BreakLabel) || ( node.DefaultLabel == null && node.Expression.ConstantValue == null && IsTraditionalSwitch(node) ) ) { Join(ref afterSwitchState, ref initialState); } return(afterSwitchState); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { EnterStatement(node); BoundSpillSequenceBuilder builder = null; var boundExpression = VisitExpression(ref builder, node.BoundExpression); var switchSections = this.VisitList(node.SwitchSections); return(UpdateStatement(builder, node.Update(boundExpression, node.ConstantTargetOpt, node.InnerLocals, switchSections, node.BreakLabel, node.StringEquality), substituteTemps: true)); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { var oldScope = _currentScope; _currentScope = CreateOrReuseScope(node, node.InnerLocals); var result = base.VisitSwitchStatement(node); _currentScope = oldScope; return(result); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { // visit switch header VisitRvalue(node.Expression); // visit switch block VisitSwitchBlock(node); return(null); }
private LocalState VisitSwitchHeader(BoundSwitchStatement node) { // Initial value for the Break state for a switch statement is established as follows: // Break state = UnreachableState if either of the following is true: // (1) there is a default label, or // (2) the switch expression is constant and there is a matching case label. // Otherwise, the Break state = current state. // visit switch expression VisitRvalue(node.Expression); LocalState breakState = this.State; // For a switch statement, we simulate a possible jump to the switch labels to ensure that // the label is not treated as an unused label and a pending branch to the label is noted. // However, if switch expression is a constant, we must have determined the single target label // at bind time, i.e. node.ConstantTargetOpt, and we must simulate a jump only to this label. var constantTargetOpt = node.ConstantTargetOpt; if ((object)constantTargetOpt == null) { bool hasDefaultLabel = false; foreach (var section in node.SwitchSections) { foreach (var boundSwitchLabel in section.SwitchLabels) { var label = boundSwitchLabel.Label; hasDefaultLabel = hasDefaultLabel || boundSwitchLabel.ConstantValueOpt == null; SetState(breakState.Clone()); var simulatedGoto = new BoundGotoStatement(node.Syntax, label); VisitGotoStatement(simulatedGoto); } } if (hasDefaultLabel) { // Condition (1) for an unreachable break state is satisfied breakState = UnreachableState(); } } else if (!node.BreakLabel.Equals(constantTargetOpt)) { SetState(breakState.Clone()); var simulatedGoto = new BoundGotoStatement(node.Syntax, constantTargetOpt); VisitGotoStatement(simulatedGoto); // Condition (1) or (2) for an unreachable break state is satisfied breakState = UnreachableState(); } return(breakState); }
private SwitchStatementLocalRewriter( BoundSwitchStatement node, LocalRewriter localRewriter ) : base( node.Syntax, localRewriter, node.SwitchSections.SelectAsArray(section => section.Syntax), // Only add instrumentation (such as sequence points) if the node is not compiler-generated. generateInstrumentation: localRewriter.Instrument && !node.WasCompilerGenerated ) { }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { // visit switch header LocalState breakState = VisitSwitchHeader(node); SetUnreachable(); // visit switch block VisitSwitchBlock(node); IntersectWith(ref breakState, ref this.State); ResolveBreaks(breakState, node.BreakLabel); return(null); }
public override BoundStatement InstrumentSwitchStatement(BoundSwitchStatement original, BoundStatement rewritten) { SwitchStatementSyntax switchSyntax = (SwitchStatementSyntax)original.Syntax; TextSpan switchSequencePointSpan = TextSpan.FromBounds( switchSyntax.SwitchKeyword.SpanStart, switchSyntax.CloseParenToken.Span.End); return(new BoundSequencePointWithSpan( syntax: switchSyntax, statementOpt: base.InstrumentSwitchStatement(original, rewritten), span: switchSequencePointSpan, hasErrors: false)); }
private void VisitSwitchBlock(BoundSwitchStatement node) { var initialState = State.Clone(); var reachableLabels = node.DecisionDag.ReachableLabels; foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { if (reachableLabels.Contains(label.Label) || label.HasErrors || label == node.DefaultLabel && node.Expression.ConstantValue == null && IsTraditionalSwitch(node)) { SetState(initialState.Clone()); } else { SetUnreachable(); } VisitPattern(label.Pattern); SetState(StateWhenTrue); if (label.WhenClause != null) { VisitCondition(label.WhenClause); SetState(StateWhenTrue); } PendingBranches.Add(new PendingBranch(label, this.State, label.Label)); } } // visit switch sections var afterSwitchState = UnreachableState(); var switchSections = node.SwitchSections; var iLastSection = (switchSections.Length - 1); for (var iSection = 0; iSection <= iLastSection; iSection++) { VisitSwitchSection(switchSections[iSection], iSection == iLastSection); // Even though it is illegal for the end of a switch section to be reachable, in erroneous // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState). Join(ref afterSwitchState, ref this.State); } if (reachableLabels.Contains(node.BreakLabel) || node.DefaultLabel == null && IsTraditionalSwitch(node)) { Join(ref afterSwitchState, ref initialState); } ResolveBreaks(afterSwitchState, node.BreakLabel); }
private BoundStatement MakeSwitchStatement( CSharpSyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray<BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray<LocalSymbol> locals, GeneratedLabelSymbol breakLabel, BoundSwitchStatement oldNode) { Debug.Assert(oldNode != null); Debug.Assert((object)rewrittenExpression.Type != null); return rewrittenExpression.Type.IsNullableType() ? MakeSwitchStatementWithNullableExpression(syntax, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, breakLabel, oldNode) : MakeSwitchStatementWithNonNullableExpression(syntax, null, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, breakLabel, oldNode); }
private BoundStatement MakeSwitchStatement( CSharpSyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray <BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray <LocalSymbol> locals, GeneratedLabelSymbol breakLabel, BoundSwitchStatement oldNode) { Debug.Assert(oldNode != null); Debug.Assert((object)rewrittenExpression.Type != null); return(rewrittenExpression.Type.IsNullableType() ? MakeSwitchStatementWithNullableExpression(syntax, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, breakLabel, oldNode) : MakeSwitchStatementWithNonNullableExpression(syntax, rewrittenExpression, rewrittenSections, constantTargetOpt, locals, breakLabel, oldNode)); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { var locals = node.InnerLocals; if (locals.IsEmpty) { // no variables declared inside the switch statement. return(base.VisitSwitchStatement(node)); } var previousBlock = PushBlock(node, locals); var result = base.VisitSwitchStatement(node); PopBlock(previousBlock); return(result); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { if (node.InnerLocals.IsDefaultOrEmpty) { // Skip introducing a new scope if there are no new locals return(base.VisitSwitchStatement(node)); } var oldScope = _currentScope; _currentScope = CreateOrReuseScope(node, node.InnerLocals); var result = base.VisitSwitchStatement(node); _currentScope = oldScope; return(result); }
private void VisitSwitchBlock(BoundSwitchStatement node) { var afterSwitchState = UnreachableState(); var switchSections = node.SwitchSections; var iLastSection = (switchSections.Length - 1); // visit switch sections for (var iSection = 0; iSection <= iLastSection; iSection++) { VisitSwitchSection(switchSections[iSection], iSection == iLastSection); // Even though it is illegal for the end of a switch section to be reachable, in erroneous // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState). IntersectWith(ref afterSwitchState, ref this.State); } SetState(afterSwitchState); }
protected override LocalState VisitSwitchStatementDispatch(BoundSwitchStatement node) { // first, learn from any null tests in the patterns int slot = node.Expression.IsSuppressed ? GetOrCreatePlaceholderSlot(node.Expression) : MakeSlot(node.Expression); if (slot > 0) { var originalInputType = node.Expression.Type; foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { LearnFromAnyNullPatterns(slot, originalInputType, label.Pattern); } } } // visit switch header Visit(node.Expression); var expressionState = ResultType; var initialState = PossiblyConditionalState.Create(this); DeclareLocals(node.InnerLocals); foreach (var section in node.SwitchSections) { // locals can be alive across jumps in the switch sections, so we declare them early. DeclareLocals(section.Locals); } var labelStateMap = LearnFromDecisionDag(node.Syntax, node.DecisionDag, node.Expression, expressionState, ref initialState); foreach (var section in node.SwitchSections) { foreach (var label in section.SwitchLabels) { var labelResult = labelStateMap.TryGetValue(label.Label, out var s1) ? s1 : (state : UnreachableState(), believedReachable : false); SetState(labelResult.state); PendingBranches.Add(new PendingBranch(label, this.State, label.Label)); } } var afterSwitchState = labelStateMap.TryGetValue(node.BreakLabel, out var stateAndReachable) ? stateAndReachable.state : UnreachableState(); labelStateMap.Free(); return(afterSwitchState); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { // dispatch to the switch sections var(initialState, afterSwitchState) = VisitSwitchStatementDispatch(node); // visit switch sections var switchSections = node.SwitchSections; var iLastSection = (switchSections.Length - 1); for (var iSection = 0; iSection <= iLastSection; iSection++) { VisitSwitchSection(switchSections[iSection], iSection == iLastSection); // Even though it is illegal for the end of a switch section to be reachable, in erroneous // code it may be reachable. We treat that as an implicit break (branch to afterSwitchState). Join(ref afterSwitchState, ref this.State); } ResolveBreaks(afterSwitchState, node.BreakLabel); return(null); }
private BoundStatement MakeSwitchStatementWithNonNullableExpression( CSharpSyntaxNode syntax, BoundStatement preambleOpt, BoundExpression rewrittenExpression, ImmutableArray<BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray<LocalSymbol> locals, 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 = GetSpecialTypeMethod(syntax, SpecialMember.System_String__op_Equality); } return oldNode.Update( loweredPreambleOpt: preambleOpt, boundExpression: rewrittenExpression, constantTargetOpt: constantTargetOpt, innerLocals: locals, switchSections: rewrittenSections, breakLabel: breakLabel, stringEquality: stringEquality); }
public virtual BoundStatement InstrumentSwitchStatement(BoundSwitchStatement original, BoundStatement rewritten) { Debug.Assert(original.Syntax.Kind() == SyntaxKind.SwitchStatement); return InstrumentStatement(original, rewritten); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { var locals = node.InnerLocals; if (locals.IsEmpty) { // no variables declared inside the switch statement. return base.VisitSwitchStatement(node); } var previousBlock = PushBlock(node, locals); var result = base.VisitSwitchStatement(node); PopBlock(previousBlock); return result; }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { if (IsInside) { _labelsInside.Add(node.BreakLabel); } return base.VisitSwitchStatement(node); }
public override BoundExpression InstrumentSwitchStatementExpression(BoundSwitchStatement original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory) { return Previous.InstrumentSwitchStatementExpression(original, rewrittenExpression, factory); }
private BoundStatement MakeSwitchStatementWithNullableExpression( CSharpSyntaxNode syntax, BoundExpression rewrittenExpression, ImmutableArray<BoundSwitchSection> rewrittenSections, LabelSymbol constantTargetOpt, ImmutableArray<LocalSymbol> locals, 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 = GetNullableMethod(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, breakLabel, oldNode); statementBuilder.Add(rewrittenSwitchStatement); return new BoundBlock(syntax, locals: (object)tempLocal == null ? ImmutableArray<LocalSymbol>.Empty : ImmutableArray.Create<LocalSymbol>(tempLocal), statements: statementBuilder.ToImmutableAndFree()); }
public override BoundStatement InstrumentSwitchStatement(BoundSwitchStatement original, BoundStatement rewritten) { SwitchStatementSyntax switchSyntax = (SwitchStatementSyntax)original.Syntax; TextSpan switchSequencePointSpan = TextSpan.FromBounds( switchSyntax.SwitchKeyword.SpanStart, switchSyntax.CloseParenToken.Span.End); return new BoundSequencePointWithSpan( syntax: switchSyntax, statementOpt: base.InstrumentSwitchStatement(original, rewritten), span: switchSequencePointSpan, hasErrors: false); }
public virtual BoundExpression InstrumentSwitchStatementExpression(BoundSwitchStatement original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory) { Debug.Assert(!original.WasCompilerGenerated); Debug.Assert(original.Syntax.Kind() == SyntaxKind.SwitchStatement); Debug.Assert(factory != null); return rewrittenExpression; }
public override BoundExpression InstrumentSwitchStatementExpression(BoundSwitchStatement original, BoundExpression rewrittenExpression, SyntheticBoundNodeFactory factory) { // 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. return AddConditionSequencePoint(base.InstrumentSwitchStatementExpression(original, rewrittenExpression, factory), original.Syntax, factory); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { BoundSpillSequence2 ss = null; BoundExpression boundExpression = VisitExpression(ref ss, node.BoundExpression); ImmutableArray<BoundSwitchSection> switchSections = (ImmutableArray<BoundSwitchSection>)this.VisitList(node.SwitchSections); return UpdateStatement(ss, node.Update(node.OuterLocals, boundExpression, node.ConstantTargetOpt, node.InnerLocals, switchSections, node.BreakLabel, node.StringEquality)); }
public override BoundStatement InstrumentSwitchStatement(BoundSwitchStatement original, BoundStatement rewritten) { return AddDynamicAnalysis(original, base.InstrumentSwitchStatement(original, rewritten)); }
// 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 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 // hash function to hash the string constants corresponding to the case labels. // See SwitchStringJumpTableEmitter.ComputeStringHash(). // We need to emit this same function to compute the hash value into the compiler generated // <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. private void LowerBasicSwitch(DecisionTree.ByValue byValue) { var switchSections = ArrayBuilder<BoundSwitchSection>.GetInstance(); var noValueMatches = _factory.GenerateLabel("noValueMatches"); var underlyingSwitchType = byValue.Type.IsEnumType() ? byValue.Type.GetEnumUnderlyingType() : byValue.Type; foreach (var vd in byValue.ValueAndDecision) { var value = vd.Key; var decision = vd.Value; var constantValue = ConstantValue.Create(value, underlyingSwitchType.SpecialType); var constantExpression = new BoundLiteral(_factory.Syntax, constantValue, underlyingSwitchType); var label = _factory.GenerateLabel("case+" + value); var switchLabel = new BoundSwitchLabel(_factory.Syntax, label, constantExpression, constantValue); var forValue = ArrayBuilder<BoundStatement>.GetInstance(); LowerDecisionTree(byValue.Expression, decision, forValue); if (!decision.MatchIsComplete) { forValue.Add(_factory.Goto(noValueMatches)); } var section = new BoundSwitchSection(_factory.Syntax, ImmutableArray.Create(switchLabel), forValue.ToImmutableAndFree()); switchSections.Add(section); } var rewrittenSections = switchSections.ToImmutableAndFree(); MethodSymbol stringEquality = null; if (underlyingSwitchType.SpecialType == SpecialType.System_String) { LocalRewriter.EnsureStringHashFunction(rewrittenSections, _factory.Syntax); stringEquality = LocalRewriter.GetSpecialTypeMethod(_factory.Syntax, SpecialMember.System_String__op_Equality); } // The BoundSwitchStatement requires a constant target when there are no sections, so we accomodate that here. var constantTarget = rewrittenSections.IsEmpty ? noValueMatches : null; var switchStatement = new BoundSwitchStatement( _factory.Syntax, null, _factory.Convert(underlyingSwitchType, byValue.Expression), constantTarget, ImmutableArray<LocalSymbol>.Empty, ImmutableArray<LocalFunctionSymbol>.Empty, rewrittenSections, noValueMatches, stringEquality); // The bound switch statement implicitly defines the label noValueMatches at the end, so we do not add it explicitly. switch (underlyingSwitchType.SpecialType) { case SpecialType.System_Boolean: // boolean switch is handled in LowerBooleanSwitch, not here. throw ExceptionUtilities.Unreachable; case SpecialType.System_String: case SpecialType.System_Byte: case SpecialType.System_Char: case SpecialType.System_Int16: case SpecialType.System_Int32: case SpecialType.System_Int64: case SpecialType.System_SByte: case SpecialType.System_UInt16: case SpecialType.System_UInt32: case SpecialType.System_UInt64: { // emit knows how to efficiently generate code for these kinds of switches. _loweredDecisionTree.Add(switchStatement); break; } default: { // other types, such as float, double, and decimal, are not currently // handled by emit and must be lowered here. _loweredDecisionTree.Add(LowerNonprimitiveSwitch(switchStatement)); break; } } LowerDecisionTree(byValue.Expression, byValue.Default); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { CollectLabel(node.ConstantTargetOpt); CollectLabel(node.BreakLabel); return base.VisitSwitchStatement(node); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { AddTarget(node.BreakLabel); return base.VisitSwitchStatement(node); }
public override BoundStatement InstrumentSwitchStatement(BoundSwitchStatement original, BoundStatement rewritten) { return Previous.InstrumentSwitchStatement(original, rewritten); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { Debug.Assert(node.OuterLocals.IsEmpty); return PossibleIteratorScope(node.InnerLocals, () => (BoundStatement)base.VisitSwitchStatement(node)); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { return PossibleIteratorScope(node.LocalsOpt, () => (BoundStatement)base.VisitSwitchStatement(node)); }
private BoundStatement LowerNonprimitiveSwitch(BoundSwitchStatement switchStatement) { // Here we handle "other" types, such as float, double, and decimal, by // lowering the BoundSwitchStatement. // We compare the constant values using value.Equals(input), using ordinary // overload resolution. Note that we cannot and do not rely on switching // on the hash code, as it may not be consistent with the behavior of Equals; // see https://github.com/dotnet/coreclr/issues/6237. Also, the hash code is // not guaranteed to be the same on the compilation platform as the runtime // platform. // CONSIDER: can we improve the quality of the code using comparisons, like // we do for other numeric types, by generating a series of tests // that use divide-and-conquer to efficiently find a matching value? // If so, we should use the BoundSwitchStatement and do that in emit. // Moreover, we should be able to use `==` rather than `.Equals` // for cases (such as non-NaN) where we know the result to be the same. var rewrittenSections = switchStatement.SwitchSections; var expression = switchStatement.Expression; var noValueMatches = switchStatement.BreakLabel; Debug.Assert(switchStatement.LoweredPreambleOpt == null); Debug.Assert(switchStatement.InnerLocals.IsDefaultOrEmpty); Debug.Assert(switchStatement.InnerLocalFunctions.IsDefaultOrEmpty); Debug.Assert(switchStatement.StringEquality == null); LabelSymbol nextLabel = null; var builder = ArrayBuilder<BoundStatement>.GetInstance(); foreach (var section in rewrittenSections) { foreach (var boundSwitchLabel in section.SwitchLabels) { if (nextLabel != null) { builder.Add(_factory.Label(nextLabel)); } nextLabel = _factory.GenerateLabel("failcase+" + section.SwitchLabels[0].ConstantValueOpt.Value); Debug.Assert(boundSwitchLabel.ConstantValueOpt != null); // generate (if (value.Equals(input)) goto label; var literal = LocalRewriter.MakeLiteral(_factory.Syntax, boundSwitchLabel.ConstantValueOpt, expression.Type); var condition = _factory.InstanceCall(literal, "Equals", expression); if (!condition.HasErrors && condition.Type.SpecialType != SpecialType.System_Boolean) { var call = (BoundCall)condition; // '{1} {0}' has the wrong return type _factory.Diagnostics.Add(ErrorCode.ERR_BadRetType, boundSwitchLabel.Syntax.GetLocation(), call.Method, call.Type); } builder.Add(_factory.ConditionalGoto(condition, boundSwitchLabel.Label, true)); builder.Add(_factory.Goto(nextLabel)); } foreach (var boundSwitchLabel in section.SwitchLabels) { builder.Add(_factory.Label(boundSwitchLabel.Label)); } builder.Add(_factory.Block(section.Statements)); // this location should not be reachable. } Debug.Assert(nextLabel != null); builder.Add(_factory.Label(nextLabel)); builder.Add(_factory.Label(noValueMatches)); return _factory.Block(builder.ToImmutableAndFree()); }
public override BoundNode VisitSwitchStatement(BoundSwitchStatement node) { var expression = (BoundExpression)this.Visit(node.BoundExpression); ReadOnlyArray<BoundSwitchSection> switchSections = this.VisitList(node.SwitchSections); if (expression.Kind != BoundKind.SpillSequence) { return node.Update(expression, node.ConstantTargetOpt, node.LocalsOpt, switchSections, node.BreakLabel); } var spill = (BoundSpillSequence)expression; var newSwitchStatement = node.Update( spill.Value, node.ConstantTargetOpt, node.LocalsOpt, switchSections, node.BreakLabel); return RewriteSpillSequenceAsBlock(spill, newSwitchStatement); }