internal static void AssertIsLabeledStatementWithLabel(this BoundStatement node, LabelSymbol label) { Debug.Assert(node != null); switch (node.Kind) { case BoundKind.LabelStatement: Debug.Assert(((BoundLabelStatement)node).Label == label); break; case BoundKind.LabeledStatement: Debug.Assert(((BoundLabeledStatement)node).Label == label); break; case BoundKind.SwitchSection: foreach (var boundSwitchLabel in ((BoundSwitchSection)node).BoundSwitchLabels) { if (boundSwitchLabel.Label == label) { return; } } throw ExceptionUtilities.Unreachable; default: throw ExceptionUtilities.UnexpectedValue(node.Kind); } }
internal AsyncIteratorMethodToStateMachineRewriter(MethodSymbol method, int methodOrdinal, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, AsyncIteratorInfo asyncIteratorInfo, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, IReadOnlySet <Symbol> hoistedVariables, IReadOnlyDictionary <Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, ArrayBuilder <StateMachineStateDebugInfo> stateMachineStateDebugInfoBuilder, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, BindingDiagnosticBag diagnostics) : base(method, methodOrdinal, asyncMethodBuilderMemberCollection, F, state, builder, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, stateMachineStateDebugInfoBuilder, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics) { Debug.Assert(asyncIteratorInfo != null); _asyncIteratorInfo = asyncIteratorInfo; _currentDisposalLabel = _exprReturnLabel; _exprReturnLabelTrue = F.GenerateLabel("yieldReturn"); _iteratorStateAllocator = new ResumableStateMachineStateAllocator( slotAllocatorOpt, firstState: StateMachineState.FirstResumableAsyncIteratorState, increasing: false); }
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); }
// Can the given decision dag node, and its successors, be generated as a sequence of // linear tests with a single "golden" path to the true label and all other paths leading // to the false label? This occurs with an is-pattern expression that uses no "or" or "not" // pattern forms. static bool canProduceLinearSequence( BoundDecisionDagNode node, LabelSymbol whenTrueLabel, LabelSymbol whenFalseLabel) { while (true) { switch (node) { case BoundWhenDecisionDagNode w: Debug.Assert(w.WhenFalse is null); node = w.WhenTrue; break; case BoundLeafDecisionDagNode n: return(n.Label == whenTrueLabel); case BoundEvaluationDecisionDagNode e: node = e.Next; break; case BoundTestDecisionDagNode t: bool falseFail = IsFailureNode(t.WhenFalse, whenFalseLabel); if (falseFail == IsFailureNode(t.WhenTrue, whenFalseLabel)) { return(false); } node = falseFail ? t.WhenTrue : t.WhenFalse; break; default: throw ExceptionUtilities.UnexpectedValue(node); } } }
// returns a proxy for a label if branch must be hijacked to run finally // otherwise returns same label back public LabelSymbol ProxyLabelIfNeeded(LabelSymbol label) { // no need to proxy a label in the current frame or when we are at the root if (this.IsRoot() || (LabelsOpt != null && LabelsOpt.Contains(label))) { return(label); } var proxyLabels = this.proxyLabels; var proxiedLabels = this.proxiedLabels; if (proxyLabels == null) { this.proxyLabels = proxyLabels = new Dictionary <LabelSymbol, LabelSymbol>(); this.proxiedLabels = proxiedLabels = new List <LabelSymbol>(); } LabelSymbol proxy; if (!proxyLabels.TryGetValue(label, out proxy)) { proxy = new GeneratedLabelSymbol("proxy" + label.Name); proxyLabels.Add(label, proxy); proxiedLabels.Add(label); } return(proxy); }
protected override BoundStatement GenerateReturn(bool finished) { BoundLiteral result = this.F.Literal(!finished); if (_tryNestingLevel == 0) { return(F.Return(result)); } else { if ((object)_exitLabel == null) { _exitLabel = this.F.GenerateLabel("exitLabel"); _methodValue = F.SynthesizedLocal(result.Type); } var gotoExit = F.Goto(_exitLabel); if (finished) { // since we are finished, we need to treat this as a potential Leave gotoExit = (BoundGotoStatement)VisitGotoStatement(gotoExit); } return(this.F.Block( F.Assignment(this.F.Local(_methodValue), result), gotoExit)); } }
public BoundLabelStatement Label(LabelSymbol label) { return(new BoundLabelStatement(Syntax, label) { WasCompilerGenerated = true }); }
internal static void AssertIsLabeledStatementWithLabel(this BoundStatement node, LabelSymbol label) { Debug.Assert(node != null); switch (node.Kind) { case BoundKind.LabelStatement: Debug.Assert(((BoundLabelStatement)node).Label == label); break; case BoundKind.LabeledStatement: Debug.Assert(((BoundLabeledStatement)node).Label == label); break; case BoundKind.SwitchSection: foreach (var boundSwitchLabel in ((BoundSwitchSection)node).BoundSwitchLabels) { if (boundSwitchLabel.Label == label) { return; } } Debug.Assert(false); break; default: Debug.Assert(false); break; } }
private void ResolveLabel(BoundNode node, LabelSymbol label) { if (node.Syntax != null && RegionContains(node.Syntax.Span)) { _labelsInside.Add(label); } }
internal AsyncMethodToStateMachineRewriter( MethodSymbol method, int methodOrdinal, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, IReadOnlySet <Symbol> hoistedVariables, IReadOnlyDictionary <Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, DiagnosticBag diagnostics) : base(F, method, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false) { _method = method; _asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; _asyncMethodBuilderField = builder; _exprReturnLabel = F.GenerateLabel("exprReturn"); _exitLabel = F.GenerateLabel("exitLabel"); _exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation) ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, syntax: F.Syntax, kind: SynthesizedLocalKind.AsyncMethodReturnValue) : null; _dynamicFactory = new LoweredDynamicOperationFactory(F, methodOrdinal); _awaiterFields = new Dictionary <TypeSymbol, FieldSymbol>(TypeSymbol.EqualsIgnoringDynamicTupleNamesAndNullabilityComparer); _nextAwaiterId = slotAllocatorOpt?.PreviousAwaiterSlotCount ?? 0; }
private BoundPatternSwitchSection BindPatternSwitchSection( BoundExpression boundSwitchExpression, SwitchSectionSyntax node, Binder originalBinder, ref BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { // Bind match section labels var boundLabelsBuilder = ArrayBuilder <BoundPatternSwitchLabel> .GetInstance(); var sectionBinder = originalBinder.GetBinder(node); // this binder can bind pattern variables from the section. Debug.Assert(sectionBinder != null); var labelsByNode = LabelsByNode; foreach (var labelSyntax in node.Labels) { LabelSymbol label = labelsByNode[labelSyntax]; BoundPatternSwitchLabel boundLabel = BindPatternSwitchSectionLabel(sectionBinder, boundSwitchExpression, labelSyntax, label, ref defaultLabel, diagnostics); boundLabelsBuilder.Add(boundLabel); } // Bind switch section statements var boundStatementsBuilder = ArrayBuilder <BoundStatement> .GetInstance(); foreach (var statement in node.Statements) { boundStatementsBuilder.Add(sectionBinder.BindStatement(statement, diagnostics)); } return(new BoundPatternSwitchSection(node, sectionBinder.GetDeclaredLocalsForScope(node), boundLabelsBuilder.ToImmutableAndFree(), boundStatementsBuilder.ToImmutableAndFree())); }
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( outerLocals: ImmutableArray <LocalSymbol> .Empty, boundExpression: rewrittenExpression, constantTargetOpt: constantTargetOpt, innerLocals: locals, switchSections: rewrittenSections, breakLabel: breakLabel, stringEquality: stringEquality)); }
public LabelSymbol ProxyReturnIfNeeded( MethodSymbol containingMethod, BoundExpression valueOpt, out LocalSymbol returnValue) { returnValue = null; // no need to proxy returns at the root if (this.IsRoot()) { return(null); } var returnProxy = this.returnProxy; if (returnProxy == null) { this.returnProxy = returnProxy = new GeneratedLabelSymbol("returnProxy"); } if (valueOpt != null) { returnValue = this.returnValue; if (returnValue == null) { this.returnValue = returnValue = new SynthesizedLocal(containingMethod, valueOpt.Type, SynthesizedLocalKind.LoweringTemp); } } return(returnProxy); }
internal AsyncMethodToStateMachineRewriter( MethodSymbol method, int methodOrdinal, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, IReadOnlySet<Symbol> hoistedVariables, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, SynthesizedLocalOrdinalsDispenser synthesizedLocalOrdinals, VariableSlotAllocator slotAllocatorOpt, int nextFreeHoistedLocalSlot, DiagnosticBag diagnostics) : base(F, method, state, hoistedVariables, nonReusableLocalProxies, synthesizedLocalOrdinals, slotAllocatorOpt, nextFreeHoistedLocalSlot, diagnostics, useFinalizerBookkeeping: false) { _method = method; _asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; _asyncMethodBuilderField = builder; _exprReturnLabel = F.GenerateLabel("exprReturn"); _exitLabel = F.GenerateLabel("exitLabel"); _exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation) ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, syntax: F.Syntax, kind: SynthesizedLocalKind.AsyncMethodReturnValue) : null; _dynamicFactory = new LoweredDynamicOperationFactory(F, methodOrdinal); _awaiterFields = new Dictionary<TypeSymbol, FieldSymbol>(TypeSymbol.EqualsIgnoringDynamicComparer); _nextAwaiterId = slotAllocatorOpt?.PreviousAwaiterSlotCount ?? 0; }
private BoundBlock PendBranches( AwaitFinallyFrame frame, LocalSymbol pendingBranchVar, LabelSymbol finallyLabel) { var bodyStatements = ArrayBuilder <BoundStatement> .GetInstance(); // handle proxy labels if have any var proxiedLabels = frame.proxiedLabels; var proxyLabels = frame.proxyLabels; // skip 0 - it means we took no explicit branches int i = 1; if (proxiedLabels != null) { for (int cnt = proxiedLabels.Count; i <= cnt; i++) { var proxied = proxiedLabels[i - 1]; var proxy = proxyLabels[proxied]; PendBranch(bodyStatements, proxy, i, pendingBranchVar, finallyLabel); } } var returnProxy = frame.returnProxyLabel; if (returnProxy != null) { PendBranch(bodyStatements, returnProxy, i, pendingBranchVar, finallyLabel); } return(_F.Block(bodyStatements.ToImmutableAndFree())); }
internal AsyncMethodToClassRewriter( MethodSymbol method, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, HashSet<Symbol> variablesCaptured, Dictionary<Symbol, CapturedSymbolReplacement> initialProxies, DiagnosticBag diagnostics, bool generateDebugInfo) : base(F, method, state, variablesCaptured, initialProxies, diagnostics, useFinalizerBookkeeping: false, generateDebugInfo: generateDebugInfo) { this.method = method; this.asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; this.asyncMethodBuilderField = builder; this.exprReturnLabel = F.GenerateLabel("exprReturn"); this.exitLabel = F.GenerateLabel("exitLabel"); this.exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation) ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, GeneratedNames.AsyncExprRetValueFieldName()) : null; this.dynamicFactory = new LoweredDynamicOperationFactory(F); }
internal static void AssertIsLabeledStatementWithLabel( this BoundStatement node, LabelSymbol label ) { Debug.Assert(node != null); switch (node.Kind) { case BoundKind.LabelStatement: Debug.Assert(((BoundLabelStatement)node).Label == label); break; case BoundKind.LabeledStatement: Debug.Assert(((BoundLabeledStatement)node).Label == label); break; case BoundKind.SwitchSection: foreach (var boundSwitchLabel in ((BoundSwitchSection)node).SwitchLabels) { if (boundSwitchLabel.Label == label) { return; } } throw ExceptionUtilities.Unreachable; default: throw ExceptionUtilities.UnexpectedValue(node.Kind); } }
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())); }
protected override void VisitSwitchSectionLabel(LabelSymbol label, BoundSwitchSection node) { _labelsDefined.Add(label); base.VisitSwitchSectionLabel(label, node); // switch statement labels are always considered to be referenced _labelsUsed.Add(label); }
public BoundWhileStatement(BoundExpression condition, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel, bool addContinue = true) { Condition = condition; Body = body; BreakLabel = breakLabel; ContinueLabel = continueLabel; AddContinue = addContinue; }
public static BoundBlock Lower(BoundStatement statement, LabelSymbol endLabel = null) { Lowerer lowerer = new Lowerer(endLabel); var lowered = lowerer.RewriteStatement(statement); return(Flatten(lowered)); }
protected override LocalState LabelState(LabelSymbol label) { LocalState result = base.LabelState(label); // We don't want errors reported in one pass to suppress errors in the next pass. result.Reported = false; return(result); }
private void LowerWhenClause(BoundWhenDecisionDagNode whenClause) { // This node is used even when there is no when clause, to record bindings. In the case that there // is no when clause, whenClause.WhenExpression and whenClause.WhenFalse are null, and the syntax for this // node is the case clause. // We need to assign the pattern variables in the code where they are in scope, so we produce a branch // to the section where they are in scope and evaluate the when clause there. var whenTrue = (BoundLeafDecisionDagNode)whenClause.WhenTrue; LabelSymbol labelToSectionScope = GetDagNodeLabel(whenClause); // We need the section syntax to get the section builder from the map. Unfortunately this is a bit awkward SyntaxNode sectionSyntax = whenClause.Syntax is SwitchLabelSyntax l ? l.Parent : whenClause.Syntax; bool foundSectionBuilder = _switchArms.TryGetValue(sectionSyntax, out ArrayBuilder <BoundStatement> sectionBuilder); Debug.Assert(foundSectionBuilder); sectionBuilder.Add(_factory.Label(labelToSectionScope)); foreach (BoundPatternBinding binding in whenClause.Bindings) { BoundExpression left = _localRewriter.VisitExpression(binding.VariableAccess); // Since a switch does not add variables to the enclosing scope, the pattern variables // are locals even in a script and rewriting them should have no effect. Debug.Assert(left.Kind == BoundKind.Local && left == binding.VariableAccess); BoundExpression right = _tempAllocator.GetTemp(binding.TempContainingValue); if (left != right) { sectionBuilder.Add(_factory.Assignment(left, right)); } } var whenFalse = whenClause.WhenFalse; var trueLabel = GetDagNodeLabel(whenTrue); if (whenClause.WhenExpression != null && whenClause.WhenExpression.ConstantValue != ConstantValue.True) { _factory.Syntax = whenClause.Syntax; BoundStatement conditionalGoto = _factory.ConditionalGoto(_localRewriter.VisitExpression(whenClause.WhenExpression), trueLabel, jumpIfTrue: true); // Only add instrumentation (such as a sequence point) if the node is not compiler-generated. if (IsSwitchStatement && !whenClause.WhenExpression.WasCompilerGenerated && _localRewriter.Instrument) { conditionalGoto = _localRewriter._instrumenter.InstrumentSwitchWhenClauseConditionalGotoBody(whenClause.WhenExpression, conditionalGoto); } sectionBuilder.Add(conditionalGoto); Debug.Assert(whenFalse != null); // We hide the jump back into the decision dag, as it is not logically part of the when clause BoundStatement jump = _factory.Goto(GetDagNodeLabel(whenFalse)); sectionBuilder.Add(IsSwitchStatement ? _factory.HiddenSequencePoint(jump) : jump); } else { Debug.Assert(whenFalse == null); sectionBuilder.Add(_factory.Goto(trueLabel)); } }
private void AddTarget(LabelSymbol label) { if (_targets == null) { _targets = new HashSet <LabelSymbol>(); } _targets.Add(label); }
private void AddGoto(LabelSymbol label) { if (_gotos == null) { _gotos = new HashSet <LabelSymbol>(); } _gotos.Add(label); }
public BoundForStatement(BoundStatement assignment, BoundExpression condition, BoundExpression step, BoundStatement body, LabelSymbol breakLabel, LabelSymbol continueLabel) { Assignment = assignment; Condition = condition; Step = step; Body = body; BreakLabel = breakLabel; ContinueLabel = continueLabel; }
// Rewriting for pattern-matching switch statements. public override BoundNode VisitPatternSwitchStatement(BoundPatternSwitchStatement node) { var statements = ArrayBuilder <BoundStatement> .GetInstance(); // copy the original switch expression into a temp BoundAssignmentOperator initialStore; var switchExpressionTemp = _factory.StoreToTemp(VisitExpression(node.Expression), out initialStore, syntaxOpt: node.Expression.Syntax); statements.Add(_factory.ExpressionStatement(initialStore)); // save the default label, if and when we find it. LabelSymbol defaultLabel = null; foreach (var section in node.PatternSwitchSections) { BoundExpression sectionCondition = _factory.Literal(false); bool isDefaultSection = false; foreach (var label in section.PatternSwitchLabels) { if (label.Syntax.Kind() == SyntaxKind.DefaultSwitchLabel) { // The default label was handled in initial tail, above Debug.Assert(label.Pattern.Kind == BoundKind.WildcardPattern && label.Guard == null); isDefaultSection = true; defaultLabel = _factory.GenerateLabel("default"); continue; } var labelCondition = LowerPattern(label.Pattern, switchExpressionTemp); if (label.Guard != null) { labelCondition = _factory.LogicalAnd(labelCondition, VisitExpression(label.Guard)); } sectionCondition = _factory.LogicalOr(sectionCondition, labelCondition); } var sectionBuilder = ArrayBuilder <BoundStatement> .GetInstance(); if (isDefaultSection) { sectionBuilder.Add(_factory.Label(defaultLabel)); } sectionBuilder.AddRange(VisitList(section.Statements)); sectionBuilder.Add(_factory.Goto(node.BreakLabel)); statements.Add(_factory.If(sectionCondition, section.Locals, _factory.Block(sectionBuilder.ToImmutableAndFree()))); } if (defaultLabel != null) { statements.Add(_factory.Goto(defaultLabel)); } statements.Add(_factory.Label(node.BreakLabel)); return(_factory.Block(node.InnerLocals.Add(switchExpressionTemp.LocalSymbol), node.InnerLocalFunctions, statements.ToImmutableAndFree())); }
protected override void ResolveBranch(PendingBranch pending, LabelSymbol label, BoundStatement target, ref bool labelStateChanged) { // branches into a region are considered entry points if (IsInside && pending.Branch != null && !RegionContains(pending.Branch.Syntax.Span)) { pending.State = pending.State.Reachable ? TopState() : UnreachableState(); } base.ResolveBranch(pending, label, target, ref labelStateChanged); }
protected override void ResolveBranch(PendingBranch pending, LabelSymbol label, BoundStatement target, ref bool labelStateChanged) { // branches into a region are considered entry points if (IsInside && pending.Branch != null && !RegionContains(pending.Branch.Syntax.Span)) { pending.State = pending.State.Reachable ? ReachableState() : UnreachableState(); } base.ResolveBranch(pending, label, target, ref labelStateChanged); }
private bool GenerateSwitchDispatch(BoundDecisionDagNode node, HashSet <BoundDecisionDagNode> loweredNodes) { Debug.Assert(!loweredNodes.Contains(node)); // We only generate a switch dispatch if we have two or more value tests in a row if (!(node is BoundTestDecisionDagNode firstTestNode && firstTestNode.Test is BoundDagValueTest firstTest && firstTestNode.WhenFalse is BoundTestDecisionDagNode whenFalse && whenFalse.Test is BoundDagValueTest secondTest && !loweredNodes.Contains(whenFalse) && !this._dagNodeLabels.ContainsKey(whenFalse) && firstTest.Input == secondTest.Input && firstTest.Input.Type.IsValidV6SwitchGoverningType())) { // https://github.com/dotnet/roslyn/issues/12509 Could optimize float, double, decimal value switches. For now use if-then-else return(false); } // Gather up the (value, label) pairs, starting with the first one var cases = ArrayBuilder <(ConstantValue value, LabelSymbol label)> .GetInstance(); cases.Add((value: firstTest.Value, label: GetDagNodeLabel(firstTestNode.WhenTrue))); BoundTestDecisionDagNode previous = firstTestNode; while (previous.WhenFalse is BoundTestDecisionDagNode p && p.Test is BoundDagValueTest vd && vd.Input == firstTest.Input && !this._dagNodeLabels.ContainsKey(p) && !loweredNodes.Contains(p)) { cases.Add((value: vd.Value, label: GetDagNodeLabel(p.WhenTrue))); loweredNodes.Add(p); previous = p; } // 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 (firstTest.Input.Type.SpecialType == SpecialType.System_String) { EnsureStringHashFunction(cases.Count, node.Syntax); stringEquality = _localRewriter.UnsafeGetSpecialTypeMethod(node.Syntax, SpecialMember.System_String__op_Equality); } LabelSymbol defaultLabel = GetDagNodeLabel(previous.WhenFalse); var dispatch = new BoundSwitchDispatch( node.Syntax, _tempAllocator.GetTemp(firstTest.Input), cases.ToImmutableAndFree(), defaultLabel, stringEquality); _loweredDecisionDag.Add(dispatch); return(true); }
/// <summary> /// An async-iterator state machine has a flag indicating "dispose mode". /// We enter dispose mode by calling DisposeAsync() when the state machine is paused on a `yield return`. /// DisposeAsync() will resume execution of the state machine from that state (using existing dispatch mechanism /// to restore execution from a given state, without executing other code to get there). /// /// From there, we don't want normal code flow: /// - from `yield return`, we'll jump to the enclosing `finally` (or method exit) /// - after finishing a `finally`, we'll jump to the next enclosing `finally` (or method exit) /// /// Some `finally` clauses may have already been rewritten and extracted to a plain block (<see cref="AsyncExceptionHandlerRewriter"/>). /// In those cases, we saved the finally-entry label in <see cref="BoundTryStatement.FinallyLabelOpt"/>. /// </summary> public override BoundNode VisitTryStatement(BoundTryStatement node) { _previousDisposalLabels ??= new ArrayBuilder <LabelSymbol>(); _previousDisposalLabels.Push(_enclosingFinallyEntryOrFinallyExitOrExitLabel); var finallyExit = F.GenerateLabel("finallyExit"); _previousDisposalLabels.Push(finallyExit); if (node.FinallyBlockOpt != null) { var finallyEntry = F.GenerateLabel("finallyEntry"); _enclosingFinallyEntryOrFinallyExitOrExitLabel = finallyEntry; // Add finallyEntry label: // try // { // ... // finallyEntry: // Add finallyExit label: // finally // { // ... // finallyExit: // } node = node.Update( tryBlock: F.Block(node.TryBlock, F.Label(finallyEntry)), node.CatchBlocks, F.Block(node.FinallyBlockOpt, F.Label(finallyExit)), node.FinallyLabelOpt, node.PreferFaultHandler); } else if ((object)node.FinallyLabelOpt != null) { _enclosingFinallyEntryOrFinallyExitOrExitLabel = node.FinallyLabelOpt; } var result = (BoundStatement)base.VisitTryStatement(node); // While inside the try and catch blocks, we'll use the current finallyEntry label for disposal // As soon as we close the try and catch blocks (ie. possibly enter the finally block), we'll use the finallyExit label for disposal (restored/popped from the stack by CloseTryCatchBlocks) Debug.Assert(_enclosingFinallyEntryOrFinallyExitOrExitLabel == finallyExit); // When exiting the try statement, we restore the previous disposal label. _enclosingFinallyEntryOrFinallyExitOrExitLabel = _previousDisposalLabels.Pop(); if (node.FinallyBlockOpt != null) { // Append: // if (disposeMode) /* jump to parent's finally or exit */ result = AppendJumpToCurrentFinallyOrExit(result); } // Note: we add this jump to extracted `finally` blocks as well, using `VisitExtractedFinallyBlock` below return(result); }
protected override BoundBlock VisitFinally(BoundBlock finallyBlock) { // within a finally, continuing disposal doesn't require any jump var savedDisposalLabel = _currentDisposalLabel; _currentDisposalLabel = null; var result = base.VisitFinally(finallyBlock); _currentDisposalLabel = savedDisposalLabel; return(result); }
internal static bool NeedsLabel(this LabelSymbol label, string name1, string name2) { var labelName = label.Name; if (labelName.StartsWith("<")) { labelName = labelName.Substring(1); } return(labelName.StartsWith(name1) || labelName.StartsWith(name2)); }
private void CollectLabel(LabelSymbol label) { if ((object)label != null) { var currentLabels = this.currentLabels; if (currentLabels == null) { this.currentLabels = currentLabels = new HashSet <LabelSymbol>(); } currentLabels.Add(label); } }
/// <summary> /// Return the flow analysis state associated with a label. /// </summary> /// <param name="label"></param> /// <returns></returns> FlowAnalysisLocalState?LabelState(LabelSymbol label) { FlowAnalysisLocalState?result; if (labels.TryGetValue(label, out result)) { return(result); } result = FlowAnalysisLocalState.UnreachableState(nextVariableSlot); labels.Add(label, result); return(result); }
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); }
internal AsyncMethodToClassRewriter( MethodSymbol method, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, SyntheticBoundNodeFactory factory, FieldSymbol state, FieldSymbol builder, Dictionary<Symbol, CapturedSymbol> localProxies, DiagnosticBag diagnostics) : base(factory, state, localProxies, diagnostics) { this.method = method; this.asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; this.asyncMethodBuilderField = builder; this.exprReturnLabel = factory.GenerateLabel("exprReturn"); this.exprRetValue = method.IsGenericTaskReturningAsync() ? factory.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, GeneratedNames.AsyncExprRetValueFieldName()) : null; this.dynamicFactory = new LoweredDynamicOperationFactory(factory); }
internal AsyncMethodToStateMachineRewriter( MethodSymbol method, AsyncMethodBuilderMemberCollection asyncMethodBuilderMemberCollection, SyntheticBoundNodeFactory F, FieldSymbol state, FieldSymbol builder, IReadOnlySet<Symbol> variablesCaptured, IReadOnlyDictionary<Symbol, CapturedSymbolReplacement> nonReusableLocalProxies, DiagnosticBag diagnostics) : base(F, method, state, variablesCaptured, nonReusableLocalProxies, diagnostics, useFinalizerBookkeeping: false) { this.method = method; this.asyncMethodBuilderMemberCollection = asyncMethodBuilderMemberCollection; this.asyncMethodBuilderField = builder; this.exprReturnLabel = F.GenerateLabel("exprReturn"); this.exitLabel = F.GenerateLabel("exitLabel"); this.exprRetValue = method.IsGenericTaskReturningAsync(F.Compilation) ? F.SynthesizedLocal(asyncMethodBuilderMemberCollection.ResultType, kind: SynthesizedLocalKind.AsyncMethodReturnValue) : null; this.dynamicFactory = new LoweredDynamicOperationFactory(F); }
private void EmitStringSwitchJumpTable( BoundSwitchStatement switchStatement, KeyValuePair<ConstantValue, object>[] switchCaseLabels, LabelSymbol fallThroughLabel, LocalOrParameter key, SyntaxNode syntaxNode) { LocalDefinition keyHash = null; // Condition is necessary, but not sufficient (e.g. might be missing a special or well-known member). if (SwitchStringJumpTableEmitter.ShouldGenerateHashTableSwitch(_module, switchCaseLabels.Length)) { Debug.Assert(_module.SupportsPrivateImplClass); var privateImplClass = _module.GetPrivateImplClass(syntaxNode, _diagnostics); Cci.IReference stringHashMethodRef = privateImplClass.GetMethod(PrivateImplementationDetails.SynthesizedStringHashFunctionName); // Heuristics and well-known member availability determine the existence // of this helper. Rather than reproduce that (language-specific) logic here, // we simply check for the information we really want - whether the helper is // available. if (stringHashMethodRef != null) { // static uint ComputeStringHash(string s) // pop 1 (s) // push 1 (uint return value) // stackAdjustment = (pushCount - popCount) = 0 _builder.EmitLoad(key); _builder.EmitOpCode(ILOpCode.Call, stackAdjustment: 0); _builder.EmitToken(stringHashMethodRef, syntaxNode, _diagnostics); var UInt32Type = _module.Compilation.GetSpecialType(SpecialType.System_UInt32); keyHash = AllocateTemp(UInt32Type, syntaxNode); _builder.EmitLocalStore(keyHash); } } Cci.IReference stringEqualityMethodRef = _module.Translate(switchStatement.StringEquality, syntaxNode, _diagnostics); Cci.IMethodReference stringLengthRef = null; var stringLengthMethod = _module.Compilation.GetSpecialTypeMember(SpecialMember.System_String__Length) as MethodSymbol; if (stringLengthMethod != null && !stringLengthMethod.HasUseSiteError) { stringLengthRef = _module.Translate(stringLengthMethod, syntaxNode, _diagnostics); } SwitchStringJumpTableEmitter.EmitStringCompareAndBranch emitStringCondBranchDelegate = (keyArg, stringConstant, targetLabel) => { if (stringConstant == ConstantValue.Null) { // if (key == null) // goto targetLabel _builder.EmitLoad(keyArg); _builder.EmitBranch(ILOpCode.Brfalse, targetLabel, ILOpCode.Brtrue); } else if (stringConstant.StringValue.Length == 0 && stringLengthRef != null) { // if (key != null && key.Length == 0) // goto targetLabel object skipToNext = new object(); _builder.EmitLoad(keyArg); _builder.EmitBranch(ILOpCode.Brfalse, skipToNext, ILOpCode.Brtrue); _builder.EmitLoad(keyArg); // Stack: key --> length _builder.EmitOpCode(ILOpCode.Call, 0); var diag = DiagnosticBag.GetInstance(); _builder.EmitToken(stringLengthRef, null, diag); Debug.Assert(diag.IsEmptyWithoutResolution); diag.Free(); _builder.EmitBranch(ILOpCode.Brfalse, targetLabel, ILOpCode.Brtrue); _builder.MarkLabel(skipToNext); } else { this.EmitStringCompareAndBranch(key, syntaxNode, stringConstant, targetLabel, stringEqualityMethodRef); } }; _builder.EmitStringSwitchJumpTable( caseLabels: switchCaseLabels, fallThroughLabel: fallThroughLabel, key: key, keyHash: keyHash, emitStringCondBranchDelegate: emitStringCondBranchDelegate, computeStringHashcodeDelegate: SynthesizedStringSwitchHashMethod.ComputeStringHash); if (keyHash != null) { FreeTemp(keyHash); } }
private void EmitSwitchHeader( BoundSwitchStatement switchStatement, BoundExpression expression, KeyValuePair<ConstantValue, object>[] switchCaseLabels, LabelSymbol fallThroughLabel) { Debug.Assert(expression.ConstantValue == null); Debug.Assert((object)expression.Type != null && expression.Type.IsValidV6SwitchGoverningType()); Debug.Assert(switchCaseLabels.Length > 0); Debug.Assert(switchCaseLabels != null); LocalDefinition temp = null; LocalOrParameter key; BoundSequence sequence = null; if (expression.Kind == BoundKind.Sequence) { sequence = (BoundSequence)expression; DefineLocals(sequence); EmitSideEffects(sequence); expression = sequence.Value; } if (expression.Kind == BoundKind.SequencePointExpression) { var sequencePointExpression = (BoundSequencePointExpression)expression; EmitSequencePoint(sequencePointExpression); expression = sequencePointExpression.Expression; } switch (expression.Kind) { case BoundKind.Local: var local = ((BoundLocal)expression).LocalSymbol; if (local.RefKind == RefKind.None && !IsStackLocal(local)) { key = this.GetLocal(local); break; } goto default; case BoundKind.Parameter: var parameter = (BoundParameter)expression; if (parameter.ParameterSymbol.RefKind == RefKind.None) { key = ParameterSlot(parameter); break; } goto default; default: EmitExpression(expression, true); temp = AllocateTemp(expression.Type, expression.Syntax); _builder.EmitLocalStore(temp); key = temp; break; } // Emit switch jump table if (expression.Type.SpecialType != SpecialType.System_String) { _builder.EmitIntegerSwitchJumpTable(switchCaseLabels, fallThroughLabel, key, expression.Type.EnumUnderlyingType().PrimitiveTypeCode); } else { this.EmitStringSwitchJumpTable(switchStatement, switchCaseLabels, fallThroughLabel, key, expression.Syntax); } if (temp != null) { FreeTemp(temp); } if (sequence != null) { FreeLocals(sequence, doNotRelease: null); } }
// returns a proxy for a label if branch must be hijacked to run finally // otherwise returns same label back public LabelSymbol ProxyLabelIfNeeded(LabelSymbol label) { // no need to proxy a label in the current frame or when we are at the root if (this.IsRoot() || (labels != null && labels.Contains(label))) { return label; } var proxyLabels = this.proxyLabels; if (proxyLabels == null) { this.proxyLabels = proxyLabels = new Dictionary<LabelSymbol, LabelSymbol>(); } LabelSymbol proxy; if (!proxyLabels.TryGetValue(label, out proxy)) { proxy = new GeneratedLabelSymbol("proxy" + label.Name); proxyLabels.Add(label, proxy); } return proxy; }
private void RecordLabel(LabelSymbol label) { DummyLocal dummy; if (_dummyVariables.TryGetValue(label, out dummy)) { RecordVarRead(dummy); } else { // this is a backwards jump with nontrivial stack requirements. // just use empty. dummy = empty; _dummyVariables.Add(label, dummy); RecordVarRead(dummy); } }
private void CollectLabel(LabelSymbol label) { if ((object)label != null) { var currentLabels = this.currentLabels; if (currentLabels == null) { this.currentLabels = currentLabels = new HashSet<LabelSymbol>(); } currentLabels.Add(label); } }
public BoundSwitchLabel(CSharpSyntaxNode syntax, LabelSymbol label, bool hasErrors = false) : this(syntax, label, expressionOpt: null, hasErrors: hasErrors) { }
private BoundSwitchLabel BindSwitchSectionLabel(SwitchLabelSyntax node, Binder sectionBinder, LabelSymbol label, DiagnosticBag diagnostics) { var switchGoverningType = SwitchGoverningType; BoundExpression boundLabelExpressionOpt = null; ConstantValue labelExpressionConstant = null; // Prevent cascading diagnostics bool hasErrors = node.HasErrors; switch (node.Kind()) { case SyntaxKind.CaseSwitchLabel: var caseLabelSyntax = (CaseSwitchLabelSyntax)node; // Bind the label case expression boundLabelExpressionOpt = sectionBinder.BindValue(caseLabelSyntax.Value, diagnostics, BindValueKind.RValue); boundLabelExpressionOpt = ConvertCaseExpression(caseLabelSyntax, boundLabelExpressionOpt, sectionBinder, ref labelExpressionConstant, diagnostics); // Check for bind errors hasErrors = hasErrors || boundLabelExpressionOpt.HasAnyErrors; // SPEC: The constant expression of each case label must denote a value that // SPEC: is implicitly convertible (§6.1) to the governing type of the switch statement. if (!hasErrors && labelExpressionConstant == null) { diagnostics.Add(ErrorCode.ERR_ConstantExpected, caseLabelSyntax.Value.Location); hasErrors = true; } if (!hasErrors && (object)labelExpressionConstant != null && FindMatchingSwitchCaseLabel(labelExpressionConstant, caseLabelSyntax) != label) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, labelExpressionConstant?.GetValueToDisplay() ?? label.Name); hasErrors = true; } // LabelSymbols for all the switch case labels are created by BuildLabels(). // Fetch the matching switch case label symbols break; case SyntaxKind.CasePatternSwitchLabel: if (!node.HasErrors) { // This should not occur, because it would have been a syntax error throw ExceptionUtilities.UnexpectedValue(node.Kind()); } break; case SyntaxKind.DefaultSwitchLabel: if (GetDefaultLabel() != label) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, label.Name); hasErrors = true; } break; default: throw ExceptionUtilities.UnexpectedValue(node.Kind()); } return new BoundSwitchLabel( syntax: node, label: label, expressionOpt: boundLabelExpressionOpt, constantValueOpt: labelExpressionConstant, hasErrors: hasErrors); }
private BoundPatternSwitchLabel BindPatternSwitchSectionLabel( Binder sectionBinder, BoundExpression boundSwitchExpression, SwitchLabelSyntax node, LabelSymbol label, ref BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { switch (node.Kind()) { case SyntaxKind.CaseSwitchLabel: { var caseLabelSyntax = (CaseSwitchLabelSyntax)node; bool wasExpression; var pattern = sectionBinder.BindConstantPattern( node, boundSwitchExpression, boundSwitchExpression.Type, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression, wasSwitchCase: true); bool hasErrors = pattern.HasErrors; var constantValue = pattern.ConstantValue; if (!hasErrors && (object)constantValue != null && pattern.Value.Type == SwitchGoverningType && this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, pattern.ConstantValue.GetValueToDisplay() ?? label.Name); hasErrors = true; } // Until we've determined whether or not the switch label is reachable, we assume it // is. The caller updates isReachable after determining if the label is subsumed. const bool isReachable = true; return new BoundPatternSwitchLabel(node, label, pattern, null, isReachable, hasErrors); } case SyntaxKind.DefaultSwitchLabel: { var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; var pattern = new BoundWildcardPattern(node); bool hasErrors = pattern.HasErrors; if (defaultLabel != null) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, label.Name); hasErrors = true; } // We always treat the default label as reachable, even if the switch is complete. const bool isReachable = true; // Note that this is semantically last! The caller will place it in the decision tree // in the final position. defaultLabel = new BoundPatternSwitchLabel(node, label, pattern, null, isReachable, hasErrors); return defaultLabel; } case SyntaxKind.CasePatternSwitchLabel: { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; var pattern = sectionBinder.BindPattern( matchLabelSyntax.Pattern, boundSwitchExpression, boundSwitchExpression.Type, node.HasErrors, diagnostics, wasSwitchCase: true); return new BoundPatternSwitchLabel(node, label, pattern, matchLabelSyntax.WhenClause != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.WhenClause.Condition, diagnostics) : null, true, node.HasErrors); } default: throw ExceptionUtilities.UnexpectedValue(node); } }
private BoundPatternSwitchLabel BindPatternSwitchSectionLabel( Binder sectionBinder, BoundExpression boundSwitchExpression, SwitchLabelSyntax node, LabelSymbol label, ref BoundPatternSwitchLabel defaultLabel, DiagnosticBag diagnostics) { switch (node.Kind()) { case SyntaxKind.CaseSwitchLabel: { var caseLabelSyntax = (CaseSwitchLabelSyntax)node; bool wasExpression; var pattern = sectionBinder.BindConstantPattern( node, boundSwitchExpression, boundSwitchExpression.Type, caseLabelSyntax.Value, node.HasErrors, diagnostics, out wasExpression, wasSwitchCase: true); bool hasErrors = pattern.HasErrors; var constantValue = pattern.ConstantValue; if (!hasErrors && (object)constantValue != null && this.FindMatchingSwitchCaseLabel(constantValue, caseLabelSyntax) != label) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, pattern.ConstantValue.GetValueToDisplay() ?? label.Name); hasErrors = true; } return new BoundPatternSwitchLabel(node, label, pattern, null, hasErrors); } case SyntaxKind.DefaultSwitchLabel: { var defaultLabelSyntax = (DefaultSwitchLabelSyntax)node; var pattern = new BoundWildcardPattern(node); bool hasErrors = pattern.HasErrors; if (defaultLabel != null) { diagnostics.Add(ErrorCode.ERR_DuplicateCaseLabel, node.Location, "default"); hasErrors = true; } // Note that this is semantically last! The caller will place it in the decision tree // in the final position. defaultLabel = new BoundPatternSwitchLabel(node, label, pattern, null, hasErrors); return defaultLabel; } case SyntaxKind.CasePatternSwitchLabel: { var matchLabelSyntax = (CasePatternSwitchLabelSyntax)node; var pattern = sectionBinder.BindPattern( matchLabelSyntax.Pattern, boundSwitchExpression, boundSwitchExpression.Type, node.HasErrors, diagnostics, wasSwitchCase: true); return new BoundPatternSwitchLabel(node, label, pattern, matchLabelSyntax.WhenClause != null ? sectionBinder.BindBooleanExpression(matchLabelSyntax.WhenClause.Condition, diagnostics) : null, node.HasErrors); } default: throw ExceptionUtilities.UnexpectedValue(node); } }
protected override BoundStatement GenerateReturn(bool finished) { BoundLiteral result = this.F.Literal(!finished); if (_tryNestingLevel == 0) { return F.Return(result); } else { if ((object)_exitLabel == null) { _exitLabel = this.F.GenerateLabel("exitLabel"); _methodValue = F.SynthesizedLocal(result.Type); } var gotoExit = F.Goto(_exitLabel); if (finished) { // since we are finished, we need to treat this as a potential Leave gotoExit = (BoundGotoStatement)VisitGotoStatement(gotoExit); } return this.F.Block( F.Assignment(this.F.Local(_methodValue), result), gotoExit); } }
private BoundBlock PendBranches( AwaitFinallyFrame frame, LocalSymbol pendingBranchVar, LabelSymbol finallyLabel) { var bodyStatements = ArrayBuilder<BoundStatement>.GetInstance(); // handle proxy labels if have any var proxiedLabels = frame.proxiedLabels; var proxyLabels = frame.proxyLabels; // skip 0 - it means we took no explicit branches int i = 1; if (proxiedLabels != null) { for (int cnt = proxiedLabels.Count; i <= cnt; i++) { var proxied = proxiedLabels[i - 1]; var proxy = proxyLabels[proxied]; PendBranch(bodyStatements, proxy, i, pendingBranchVar, finallyLabel); } } var returnProxy = frame.returnProxyLabel; if (returnProxy != null) { PendBranch(bodyStatements, returnProxy, i, pendingBranchVar, finallyLabel); } return _F.Block(bodyStatements.ToImmutableAndFree()); }
private GeneratedLabelSymbol GetLabelClone(LabelSymbol label) { var labelClones = _labelClones; if (labelClones == null) { _labelClones = labelClones = new Dictionary<LabelSymbol, GeneratedLabelSymbol>(); } GeneratedLabelSymbol clone; if (!labelClones.TryGetValue(label, out clone)) { clone = new GeneratedLabelSymbol("cloned_" + label.Name); labelClones.Add(label, clone); } return clone; }
public virtual void VisitLabel(LabelSymbol symbol) { DefaultVisit(symbol); }
private void EmitSwitchHeader( BoundSwitchStatement switchStatement, BoundExpression expression, KeyValuePair<ConstantValue, object>[] switchCaseLabels, LabelSymbol fallThroughLabel) { Debug.Assert(expression.ConstantValue == null); Debug.Assert((object)expression.Type != null && expression.Type.IsValidSwitchGoverningType()); Debug.Assert(switchCaseLabels.Length > 0); Debug.Assert(switchCaseLabels != null); var exprType = expression.Type; LocalDefinition temp = null; // Emit switch jump table if (expression.Type.SpecialType != SpecialType.System_String) { if (expression.Kind == BoundKind.Local && ((BoundLocal)expression).LocalSymbol.RefKind == RefKind.None) { builder.EmitIntegerSwitchJumpTable(switchCaseLabels, fallThroughLabel, this.GetLocal((BoundLocal)expression), exprType.EnumUnderlyingType().PrimitiveTypeCode); } else if (expression.Kind == BoundKind.Parameter && ((BoundParameter)expression).ParameterSymbol.RefKind == RefKind.None) { builder.EmitIntegerSwitchJumpTable(switchCaseLabels, fallThroughLabel, ParameterSlot((BoundParameter)expression), exprType.EnumUnderlyingType().PrimitiveTypeCode); } else { EmitExpression(expression, true); temp = AllocateTemp(exprType, expression.Syntax); builder.EmitLocalStore(temp); builder.EmitIntegerSwitchJumpTable(switchCaseLabels, fallThroughLabel, temp, exprType.EnumUnderlyingType().PrimitiveTypeCode); } } else { if (expression.Kind == BoundKind.Local && ((BoundLocal)expression).LocalSymbol.RefKind == RefKind.None) { this.EmitStringSwitchJumpTable(switchStatement, switchCaseLabels, fallThroughLabel, this.GetLocal((BoundLocal)expression), expression.Syntax); } else { EmitExpression(expression, true); temp = AllocateTemp(exprType, expression.Syntax); builder.EmitLocalStore(temp); this.EmitStringSwitchJumpTable(switchStatement, switchCaseLabels, fallThroughLabel, temp, expression.Syntax); } } if (temp != null) { FreeTemp(temp); } }
// called on branches and labels private void RecordBranch(LabelSymbol label) { DummyLocal dummy; if (_dummyVariables.TryGetValue(label, out dummy)) { RecordVarRead(dummy); } else { // create a dummy and start tracing it dummy = new DummyLocal(); _dummyVariables.Add(label, dummy); _locals.Add(dummy, new LocalDefUseInfo(StackDepth())); RecordVarWrite(dummy); } }
private static KeyValuePair<ConstantValue, object>[] GetSwitchCaseLabels(ImmutableArray<BoundSwitchSection> sections, ref LabelSymbol fallThroughLabel) { var labelsBuilder = ArrayBuilder<KeyValuePair<ConstantValue, object>>.GetInstance(); foreach (var section in sections) { foreach (BoundSwitchLabel boundLabel in section.BoundSwitchLabels) { var label = (SourceLabelSymbol)boundLabel.Label; if (label.IdentifierNodeOrToken.CSharpKind() == SyntaxKind.DefaultSwitchLabel) { fallThroughLabel = label; } else { Debug.Assert(label.SwitchCaseLabelConstant != null && SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(label.SwitchCaseLabelConstant)); labelsBuilder.Add(new KeyValuePair<ConstantValue, object>(label.SwitchCaseLabelConstant, label)); } } } return labelsBuilder.ToArrayAndFree(); }
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()); }
private static KeyValuePair<ConstantValue, object>[] GetSwitchCaseLabels(ImmutableArray<BoundSwitchSection> sections, ref LabelSymbol fallThroughLabel) { var labelsBuilder = ArrayBuilder<KeyValuePair<ConstantValue, object>>.GetInstance(); foreach (var section in sections) { foreach (BoundSwitchLabel boundLabel in section.SwitchLabels) { var label = boundLabel.Label; if (boundLabel.ConstantValueOpt == null) { fallThroughLabel = label; } else { var value = boundLabel.ConstantValueOpt; Debug.Assert(value != null && SwitchConstantValueHelper.IsValidSwitchCaseLabelConstant(value)); labelsBuilder.Add(new KeyValuePair<ConstantValue, object>(value, label)); } } } return labelsBuilder.ToArrayAndFree(); }
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); }
private void EmitConstantSwitchHeader(BoundExpression expression, LabelSymbol target) { EmitExpression(expression, false); _builder.EmitBranch(ILOpCode.Br, target); }
public BoundLabelStatement Label(LabelSymbol label) { return new BoundLabelStatement(Syntax, label) { WasCompilerGenerated = true }; }
private void PendBranch( ArrayBuilder<BoundStatement> bodyStatements, LabelSymbol proxy, int i, LocalSymbol pendingBranchVar, LabelSymbol finallyLabel) { // branch lands here bodyStatements.Add(_F.Label(proxy)); // pend the branch bodyStatements.Add(_F.Assignment(_F.Local(pendingBranchVar), _F.Literal(i))); // skip other proxies bodyStatements.Add(_F.Goto(finallyLabel)); }