private static BoundStatement RewriteIfStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> locals,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenConsequence,
            BoundStatement rewrittenAlternativeOpt,
            bool hasErrors)
        {
            var afterif = new GeneratedLabelSymbol("afterif");
            var builder = ArrayBuilder<BoundStatement>.GetInstance();

            if (rewrittenAlternativeOpt == null)
            {
                // if (condition) 
                //   consequence;  
                //
                // becomes
                //
                // GotoIfFalse condition afterif;
                // consequence;
                // afterif:

                builder.Add(new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, afterif));
                builder.Add(rewrittenConsequence);
            }
            else
            {
                // if (condition)
                //     consequence;
                // else 
                //     alternative
                //
                // becomes
                //
                // GotoIfFalse condition alt;
                // consequence
                // goto afterif;
                // alt:
                // alternative;
                // afterif:

                var alt = new GeneratedLabelSymbol("alternative");

                builder.Add(new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, alt));
                builder.Add(rewrittenConsequence);
                builder.Add(new BoundGotoStatement(syntax, afterif));
                builder.Add(new BoundLabelStatement(syntax, alt));
                builder.Add(rewrittenAlternativeOpt);
            }

            builder.Add(new BoundLabelStatement(syntax, afterif));

            if (!locals.IsDefaultOrEmpty)
            {
                return new BoundBlock(syntax, locals, builder.ToImmutableAndFree(), hasErrors);
            }

            return new BoundStatementList(syntax, builder.ToImmutableAndFree(), hasErrors);
        }
        private BoundStatement RewriteWhileStatement(
            BoundLoopStatement loop,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(loop.Kind == BoundKind.WhileStatement || loop.Kind == BoundKind.ForEachStatement);

            // while (condition) 
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start: 
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            SyntaxNode syntax = loop.Syntax;
            var startLabel = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);

            if (this.Instrument && !loop.WasCompilerGenerated)
            {
                switch (loop.Kind)
                {
                    case BoundKind.WhileStatement:
                        ifConditionGotoStart = _instrumenter.InstrumentWhileStatementConditionalGotoStartOrBreak((BoundWhileStatement)loop, ifConditionGotoStart);
                        break;

                    case BoundKind.ForEachStatement:
                        ifConditionGotoStart = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)loop, ifConditionGotoStart);
                        break;

                    default:
                        throw ExceptionUtilities.UnexpectedValue(loop.Kind);
                }

                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                gotoContinue,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, continueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, breakLabel));
        }
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody = (BoundStatement)Visit(node.Body);
            var startLabel = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            // 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 condition are being executed.
            if (!node.WasCompilerGenerated && this.Instrument)
            {
                rewrittenCondition = _instrumenter.InstrumentDoStatementCondition(node, rewrittenCondition, _factory);
            }

            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, rewrittenCondition, true, startLabel);

            if (!node.WasCompilerGenerated && this.Instrument)
            {
                ifConditionGotoStart = _instrumenter.InstrumentDoStatementConditionalGotoStart(node, ifConditionGotoStart);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start: 
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            if (node.Locals.IsEmpty)
            {
                return BoundStatementList.Synthesized(syntax, node.HasErrors,
                    new BoundLabelStatement(syntax, startLabel),
                    rewrittenBody,
                    new BoundLabelStatement(syntax, node.ContinueLabel),
                    ifConditionGotoStart,
                    new BoundLabelStatement(syntax, node.BreakLabel));
            }

            return BoundStatementList.Synthesized(syntax, node.HasErrors,
                new BoundLabelStatement(syntax, startLabel),
                new BoundBlock(syntax,
                               node.Locals,
                               ImmutableArray.Create<BoundStatement>(rewrittenBody,
                                                                     new BoundLabelStatement(syntax, node.ContinueLabel),
                                                                     ifConditionGotoStart)),
                new BoundLabelStatement(syntax, node.BreakLabel));
        }
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody = (BoundStatement)Visit(node.Body);
            var startLabel = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, AddConditionSequencePoint(rewrittenCondition, node), true, startLabel);

            if (this.GenerateDebugInfo)
            {
                var doSyntax = (DoStatementSyntax)syntax;
                var span = TextSpan.FromBounds(
                    doSyntax.WhileKeyword.SpanStart,
                    doSyntax.SemicolonToken.Span.End);

                ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start: 
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            if (!node.InnerLocals.IsDefaultOrEmpty)
            {
                return BoundStatementList.Synthesized(syntax, node.HasErrors,
                    new BoundLabelStatement(syntax, startLabel),
                    new BoundBlock(syntax,
                                   node.InnerLocals,
                                   ImmutableArray.Create<BoundStatement>(rewrittenBody,
                                                                         new BoundLabelStatement(syntax, node.ContinueLabel),
                                                                         ifConditionGotoStart)),
                    new BoundLabelStatement(syntax, node.BreakLabel));
            }

            return BoundStatementList.Synthesized(syntax, node.HasErrors,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, node.ContinueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, node.BreakLabel));
        }
        private static BoundStatement RewriteIfStatement(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenConsequence,
            BoundStatement rewrittenAlternativeOpt,
            bool hasErrors)
        {
            var afterif = new GeneratedLabelSymbol("afterif");

            // if (condition) 
            //   consequence;  
            //
            // becomes
            //
            // GotoIfFalse condition afterif;
            // consequence;
            // afterif:

            if (rewrittenAlternativeOpt == null)
            {
                return BoundStatementList.Synthesized(syntax,
                    new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, afterif),
                    rewrittenConsequence,
                    new BoundLabelStatement(syntax, afterif));
            }

            // if (condition)
            //     consequence;
            // else 
            //     alternative
            //
            // becomes
            //
            // GotoIfFalse condition alt;
            // consequence
            // goto afterif;
            // alt:
            // alternative;
            // afterif:

            var alt = new GeneratedLabelSymbol("alternative");
            return BoundStatementList.Synthesized(syntax, hasErrors,
                new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, alt),
                rewrittenConsequence,
                new BoundGotoStatement(syntax, afterif),
                new BoundLabelStatement(syntax, alt),
                rewrittenAlternativeOpt,
                new BoundLabelStatement(syntax, afterif));
        }
        private BoundStatement RewriteWhileStatement(
            CSharpSyntaxNode syntax,
            BoundExpression rewrittenCondition,
            TextSpan conditionSequencePointSpan,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            var startLabel = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);

            if (this.GenerateDebugInfo)
            {
                ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan);
            }

            // while (condition) 
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start: 
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);
            if (this.GenerateDebugInfo)
            {
                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                gotoContinue,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, continueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, 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);
        }
        public override BoundNode VisitDoStatement(BoundDoStatement node)
        {
            Debug.Assert(node != null);

            var rewrittenCondition = (BoundExpression)Visit(node.Condition);
            var rewrittenBody = (BoundStatement)Visit(node.Body);
            var startLabel = new GeneratedLabelSymbol("start");

            var syntax = node.Syntax;

            // 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 condition are being executed.
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(syntax, AddConditionSequencePoint(rewrittenCondition, node), true, startLabel);

            if (this.GenerateDebugInfo)
            {
                var doSyntax = (DoStatementSyntax)syntax;
                var span = TextSpan.FromBounds(
                    doSyntax.WhileKeyword.SpanStart,
                    doSyntax.SemicolonToken.Span.End);

                ifConditionGotoStart = new BoundSequencePointWithSpan(doSyntax, ifConditionGotoStart, span);
            }

            // do
            //   body
            // while (condition);
            //
            // becomes
            //
            // start: 
            // {
            //   body
            //   continue:
            //   sequence point
            //   GotoIfTrue condition start;
            // }
            // break:

            return BoundStatementList.Synthesized(syntax, node.HasErrors,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, node.ContinueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, node.BreakLabel));
        }
        public BoundStatement Switch(BoundExpression ex, params BoundSwitchSection[] sections)
        {
            Debug.Assert(ex.Type.SpecialType != Microsoft.CodeAnalysis.SpecialType.System_String); // BoundSwitchStatement.StringEquality not set

            if (sections.Length == 0) return ExpressionStatement(ex);
            GeneratedLabelSymbol breakLabel = new GeneratedLabelSymbol("break");
            var s = ImmutableArray.Create<BoundSwitchSection>(sections);
            CheckSwitchSections(s);
            return new BoundSwitchStatement(
                Syntax,
                ImmutableArray<LocalSymbol>.Empty,
                ex,
                null,
                ImmutableArray<LocalSymbol>.Empty,
                s,
                breakLabel,
                null) { WasCompilerGenerated = true };
        }
 public BoundStatement If(BoundExpression condition, BoundStatement thenClause, BoundStatement elseClause)
 {
     // We translate
     //    if (condition) thenClause else elseClause
     // as
     //    {
     //       ConditionalGoto(!condition) alternative
     //       thenClause
     //       goto afterif;
     //       alternative:
     //       elseClause
     //       afterif:
     //    }
     Debug.Assert(thenClause != null && elseClause != null);
     var afterif = new GeneratedLabelSymbol("afterif");
     var alt = new GeneratedLabelSymbol("alternative");
     return Block(
         new BoundConditionalGoto(Syntax, condition, false, alt) { WasCompilerGenerated = true },
         thenClause,
         Goto(afterif),
         Label(alt),
         elseClause,
         Label(afterif)
         );
 }
Esempio n. 11
0
        private BoundStatement RewriteForStatement(
            BoundLoopStatement original,
            ImmutableArray<LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(original.Kind == BoundKind.ForStatement || original.Kind == BoundKind.ForEachStatement);
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like 
            //
            // for([|int i = 0, j = 0|]; ; [|i++, j++|])
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementors.
            //
            // We now make each one individually a sequence point:
            //
            // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|])
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementors.

            SyntaxNode syntax = original.Syntax;
            var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            var endLabel = new GeneratedLabelSymbol("end");

            //  initializer;
            //  goto end;

            BoundStatement gotoEnd = new BoundGotoStatement(syntax, endLabel);

            if (this.Instrument)
            {
                switch (original.Kind)
                {
                    case BoundKind.ForEachStatement:
                        gotoEnd = _instrumenter.InstrumentForEachStatementGotoEnd((BoundForEachStatement)original, gotoEnd);
                        break;
                    case BoundKind.ForStatement:
                        gotoEnd = _instrumenter.InstrumentForStatementGotoEnd((BoundForStatement)original, gotoEnd);
                        break;
                    default:
                        throw ExceptionUtilities.UnexpectedValue(original.Kind);
                }
            }

            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;
            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.Instrument)
            {
                switch (original.Kind)
                {
                    case BoundKind.ForEachStatement:
                        branchBack = _instrumenter.InstrumentForEachStatementConditionalGotoStart((BoundForEachStatement)original, branchBack);
                        break;
                    case BoundKind.ForStatement:
                        branchBack = _instrumenter.InstrumentForStatementConditionalGotoStart((BoundForStatement)original, branchBack);
                        break;
                    default:
                        throw ExceptionUtilities.UnexpectedValue(original.Kind);
                }
            }

            statementBuilder.Add(branchBack);

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();
            return new BoundBlock(syntax, outerLocals, ImmutableArray<LocalFunctionSymbol>.Empty, statements, hasErrors);
        }
Esempio n. 12
0
 protected SwitchBinder(Binder next, SwitchStatementSyntax switchSyntax)
     : base(next)
 {
     SwitchSyntax = switchSyntax;
     _breakLabel = new GeneratedLabelSymbol("break");
 }
        private BoundStatement RewriteForStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            ImmutableArray<LocalSymbol> innerLocals,
            BoundExpression rewrittenCondition,
            SyntaxNodeOrToken conditionSyntax,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like 
            //
            // for(int i = 0, j = 0; ; i++, j++)
            //     ^--------------^    ^------^   
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementers.
            //
            // We now make each one individually a sequence point:
            //
            // for(int i = 0, j = 0; ; i++, j++)
            //     ^-------^  ^---^    ^-^  ^-^
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementers.

            var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            if (!innerLocals.IsDefaultOrEmpty)
            {
                var walker = new AnyLocalCapturedInALambdaWalker(innerLocals);

                if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenIncrement) || walker.Analyze(rewrittenBody))
                {
                    // If any inner local is captured within a lambda, we need to enter scope-block
                    // always from the top, that is where an instance of a display class will be created.
                    // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas.

                    // for (initializer; condition; increment)
                    //   body;
                    //
                    // becomes the following (with
                    // block added for locals)
                    //
                    // {
                    //   initializer;
                    // start:
                    //   {
                    //     GotoIfFalse condition break;
                    //     body;
                    // continue:
                    //     increment;
                    //     goto start;
                    //   }
                    // break:
                    // }

                    // start:
                    statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

                    var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();

                    //   GotoIfFalse condition break;
                    if (rewrittenCondition != null)
                    {
                        BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

                        if (this.generateDebugInfo)
                        {
                            if (conditionSyntax.IsToken)
                            {
                                ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSyntax.Span);
                            }
                            else
                            {
                                ifNotConditionGotoBreak = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), ifNotConditionGotoBreak);
                            }
                        }

                        blockBuilder.Add(ifNotConditionGotoBreak);
                    }

                    // body;
                    blockBuilder.Add(rewrittenBody);

                    // continue:
                    //   increment;
                    blockBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
                    if (rewrittenIncrement != null)
                    {
                        blockBuilder.Add(rewrittenIncrement);
                    }

                    //     goto start;
                    blockBuilder.Add(new BoundGotoStatement(syntax, startLabel));

                    statementBuilder.Add(new BoundBlock(syntax, innerLocals, blockBuilder.ToImmutableAndFree()));

                    // break:
                    statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

                    return new BoundBlock(syntax, outerLocals, statementBuilder.ToImmutableAndFree(), hasErrors);
                }
            }

            var endLabel = new GeneratedLabelSymbol("end");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with
            // block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            //  initializer;
            //  goto end;

            //mark the initial jump as hidden.
            //We do it to tell that this is not a part of previous statement.
            //This jump may be a target of another jump (for example if loops are nested) and that will make 
            //impression of the previous statement being re-executed
            var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel));
            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            ArrayBuilder<BoundStatement> saveBuilder = null;

            if (!innerLocals.IsDefaultOrEmpty)
            {
                saveBuilder = statementBuilder;
                statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            }

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;
            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.generateDebugInfo)
            {
                if (conditionSyntax.IsToken)
                {
                    branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSyntax.Span);
                }
                else
                {
                    //if there is no condition, make this a hidden point so that 
                    //it does not count as a part of previous statement
                    branchBack = new BoundSequencePoint((CSharpSyntaxNode)conditionSyntax.AsNode(), branchBack);
                }
            }

            statementBuilder.Add(branchBack);

            if (!innerLocals.IsDefaultOrEmpty)
            {
                var block = new BoundBlock(syntax, innerLocals, statementBuilder.ToImmutableAndFree());
                statementBuilder = saveBuilder;
                statementBuilder.Add(block);
            }

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();
            return new BoundBlock(syntax, outerLocals, statements, hasErrors);
        }
        private static LabelSymbol GetNullValueTargetSwitchLabel(ImmutableArray<BoundSwitchSection> sections, GeneratedLabelSymbol breakLabel)
        {
            LabelSymbol fallThroughLabel = breakLabel;

            foreach (var section in sections)
            {
                foreach (BoundSwitchLabel boundLabel in section.BoundSwitchLabels)
                {
                    var label = (SourceLabelSymbol)boundLabel.Label;
                    var labelConstant = label.SwitchCaseLabelConstant;

                    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;
        }
Esempio n. 15
0
            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;
            }
        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);
        }
        /// <summary>
        /// Generate a thread-safe accessor for a regular field-like event.
        /// 
        /// DelegateType tmp0 = _event; //backing field
        /// DelegateType tmp1;
        /// DelegateType tmp2;
        /// do {
        ///     tmp1 = tmp0;
        ///     tmp2 = (DelegateType)Delegate.Combine(tmp1, value); //Remove for -=
        ///     tmp0 = Interlocked.CompareExchange&lt;DelegateType&gt;(ref _event, tmp2, tmp1);
        /// } while ((object)tmp0 != (object)tmp1);
        /// </summary>
        internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEventSymbol eventSymbol, bool isAddMethod, CSharpCompilation compilation, DiagnosticBag diagnostics)
        {
            CSharpSyntaxNode syntax = eventSymbol.CSharpSyntaxNode;

            TypeSymbol delegateType = eventSymbol.Type;
            MethodSymbol accessor = isAddMethod ? eventSymbol.AddMethod : eventSymbol.RemoveMethod;
            ParameterSymbol thisParameter = accessor.ThisParameter;

            TypeSymbol boolType = compilation.GetSpecialType(SpecialType.System_Boolean);
            MethodSymbol updateMethod = (MethodSymbol)compilation.GetSpecialTypeMember(isAddMethod ? SpecialMember.System_Delegate__Combine : SpecialMember.System_Delegate__Remove);
            MethodSymbol compareExchangeMethod = GetConstructedCompareExchangeMethod(delegateType, compilation, accessor.Locations[0], diagnostics);

            if ((object)compareExchangeMethod == null)
            {
                return new BoundBlock(syntax,
                    locals: ImmutableArray<LocalSymbol>.Empty,
                    statements: ImmutableArray.Create<BoundStatement>(
                        new BoundReturnStatement(syntax,
                            expressionOpt: null)
                { WasCompilerGenerated = true }))
                { WasCompilerGenerated = true };
            }

            GeneratedLabelSymbol loopLabel = new GeneratedLabelSymbol("loop");

            const int numTemps = 3;

            LocalSymbol[] tmps = new LocalSymbol[numTemps];
            BoundLocal[] boundTmps = new BoundLocal[numTemps];

            for (int i = 0; i < numTemps; i++)
            {
                tmps[i] = new SynthesizedLocal(accessor, delegateType, SynthesizedLocalKind.LoweringTemp);
                boundTmps[i] = new BoundLocal(syntax, tmps[i], null, delegateType);
            }

            BoundThisReference fieldReceiver = eventSymbol.IsStatic ?
                null :
                new BoundThisReference(syntax, thisParameter.Type) { WasCompilerGenerated = true };

            BoundFieldAccess boundBackingField = new BoundFieldAccess(syntax,
                receiver: fieldReceiver,
                fieldSymbol: eventSymbol.AssociatedField,
                constantValueOpt: null)
            { WasCompilerGenerated = true };

            BoundParameter boundParameter = new BoundParameter(syntax,
                parameterSymbol: accessor.Parameters[0])
            { WasCompilerGenerated = true };

            // tmp0 = _event;
            BoundStatement tmp0Init = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[0],
                    right: boundBackingField,
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // LOOP:
            BoundStatement loopStart = new BoundLabelStatement(syntax,
                label: loopLabel)
            { WasCompilerGenerated = true };

            // tmp1 = tmp0;
            BoundStatement tmp1Update = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[1],
                    right: boundTmps[0],
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // (DelegateType)Delegate.Combine(tmp1, value)
            BoundExpression delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax,
                operand: BoundCall.Synthesized(syntax,
                    receiverOpt: null,
                    method: updateMethod,
                    arguments: ImmutableArray.Create<BoundExpression>(boundTmps[1], boundParameter)),
                kind: ConversionKind.ExplicitReference,
                type: delegateType);

            // tmp2 = (DelegateType)Delegate.Combine(tmp1, value);
            BoundStatement tmp2Update = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[2],
                    right: delegateUpdate,
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1)
            BoundExpression compareExchange = BoundCall.Synthesized(syntax,
                receiverOpt: null,
                method: compareExchangeMethod,
                arguments: ImmutableArray.Create<BoundExpression>(boundBackingField, boundTmps[2], boundTmps[1]));

            // tmp0 = Interlocked.CompareExchange<DelegateType>(ref _event, tmp2, tmp1);
            BoundStatement tmp0Update = new BoundExpressionStatement(syntax,
                expression: new BoundAssignmentOperator(syntax,
                    left: boundTmps[0],
                    right: compareExchange,
                    type: delegateType)
            { WasCompilerGenerated = true })
            { WasCompilerGenerated = true };

            // tmp0 == tmp1 // i.e. exit when they are equal, jump to start otherwise
            BoundExpression loopExitCondition = new BoundBinaryOperator(syntax,
                operatorKind: BinaryOperatorKind.ObjectEqual,
                left: boundTmps[0],
                right: boundTmps[1],
                constantValueOpt: null,
                methodOpt: null,
                resultKind: LookupResultKind.Viable,
                type: boolType)
            { WasCompilerGenerated = true };
            
            // branchfalse (tmp0 == tmp1) LOOP
            BoundStatement loopEnd = new BoundConditionalGoto(syntax,
                condition: loopExitCondition,
                jumpIfTrue: false,
                label: loopLabel)
            { WasCompilerGenerated = true };

            BoundStatement @return = new BoundReturnStatement(syntax,
                expressionOpt: null)
            { WasCompilerGenerated = true };

            return new BoundBlock(syntax,
                locals: tmps.AsImmutable(),
                statements: ImmutableArray.Create<BoundStatement>(
                    tmp0Init,
                    loopStart,
                    tmp1Update,
                    tmp2Update,
                    tmp0Update,
                    loopEnd,
                    @return))
            { WasCompilerGenerated = true };

        }
        private BoundStatement RewriteForStatement(
            BoundForStatement node,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody)
        {
            if (node.InnerLocals.IsEmpty)
            {
                return RewriteForStatementWithoutInnerLocals(
                    node,
                    node.OuterLocals,
                    rewrittenInitializer,
                    rewrittenCondition,
                    rewrittenIncrement,
                    rewrittenBody,
                    node.BreakLabel,
                    node.ContinueLabel, node.HasErrors);
            }

            // We need to enter inner_scope-block from the top, that is where an instance of a display class will be created
            // if any local is captured within a lambda.

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            // start:
            //   {
            //     GotoIfFalse condition break;
            //     body;
            // continue:
            //     increment;
            //     goto start;
            //   }
            // break:
            // }

            Debug.Assert(rewrittenBody != null);

            SyntaxNode syntax = node.Syntax;
            var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();

            //  initializer;
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // start:
            BoundStatement startLabelStatement = new BoundLabelStatement(syntax, startLabel);

            if (Instrument)
            {
                startLabelStatement = new BoundSequencePoint(null, startLabelStatement);
            }

            statementBuilder.Add(startLabelStatement);

            var blockBuilder = ArrayBuilder<BoundStatement>.GetInstance();

            // GotoIfFalse condition break;
            if (rewrittenCondition != null)
            {
                BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, node.BreakLabel);

                if (this.Instrument)
                {
                    ifNotConditionGotoBreak = _instrumenter.InstrumentForStatementConditionalGotoStartOrBreak(node, ifNotConditionGotoBreak);
                }

                blockBuilder.Add(ifNotConditionGotoBreak);
            }

            // body;
            blockBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            blockBuilder.Add(new BoundLabelStatement(syntax, node.ContinueLabel));
            if (rewrittenIncrement != null)
            {
                blockBuilder.Add(rewrittenIncrement);
            }

            // goto start;
            blockBuilder.Add(new BoundGotoStatement(syntax, startLabel));

            statementBuilder.Add(new BoundBlock(syntax, node.InnerLocals, blockBuilder.ToImmutableAndFree()));

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, node.BreakLabel));

            var statements = statementBuilder.ToImmutableAndFree();
            return new BoundBlock(syntax, node.OuterLocals, statements, node.HasErrors);
        }
Esempio n. 19
0
 protected LoopBinder(MethodSymbol owner, Binder enclosing)
     : base(owner, enclosing)
 {
     this.breakLabel = new GeneratedLabelSymbol("break");
     this.continueLabel = new GeneratedLabelSymbol("continue");
 }
Esempio n. 20
0
 protected LoopBinder(Binder enclosing)
     : base(enclosing)
 {
     _breakLabel = new GeneratedLabelSymbol("break");
     _continueLabel = new GeneratedLabelSymbol("continue");
 }
        private BoundStatement RewriteWhileStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> innerLocals,
            BoundExpression rewrittenCondition,
            TextSpan conditionSequencePointSpan,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            if (!innerLocals.IsDefaultOrEmpty)
            {
                var walker = new AnyLocalCapturedInALambdaWalker(innerLocals);

                if (walker.Analyze(rewrittenCondition) || walker.Analyze(rewrittenBody))
                {
                    // If any inner local is captured within a lambda, we need to enter scope-block
                    // always from the top, that is where an instance of a display class will be created.
                    // The IL will be less optimal, but this shouldn't be a problem, given presence of lambdas.

                    // while (condition) 
                    //   body;
                    //
                    // becomes
                    //
                    // continue:
                    // {
                    //     GotoIfFalse condition break;
                    //     body
                    //     goto continue;
                    // }
                    // break:

                    // TODO: We could perform more fine analysis. 
                    // If locals declared in condition (the innerLocals) are captured, but not referenced in the body, we could use optimal IL by creating
                    // another block around the condition and use it as a scope for the locals declared in condition.
                    // This optimization can be applied to 'for' as well, while-body === for-body + increment.
                    // Note however that the scope adjusments will likely be observable during debugging, in locals window.

                    BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

                    if (this.GenerateDebugInfo)
                    {
                        ifNotConditionGotoBreak = new BoundSequencePointWithSpan(syntax, ifNotConditionGotoBreak, conditionSequencePointSpan);
                    }

                    return BoundStatementList.Synthesized(syntax, hasErrors,
                        new BoundLabelStatement(syntax, continueLabel),
                        new BoundBlock(syntax,
                                       innerLocals,
                                       ImmutableArray.Create(
                                            ifNotConditionGotoBreak,
                                            rewrittenBody,
                                            new BoundGotoStatement(syntax, continueLabel))),
                        new BoundLabelStatement(syntax, breakLabel));
                }
            }

            var startLabel = new GeneratedLabelSymbol("start");
            BoundStatement ifConditionGotoStart = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);

            if (this.GenerateDebugInfo)
            {
                ifConditionGotoStart = new BoundSequencePointWithSpan(syntax, ifConditionGotoStart, conditionSequencePointSpan);
            }

            // while (condition) 
            //   body;
            //
            // becomes
            //
            // goto continue;
            // start: 
            // {
            //     body
            //     continue:
            //     GotoIfTrue condition start;
            // }
            // break:

            BoundStatement gotoContinue = new BoundGotoStatement(syntax, continueLabel);
            if (this.GenerateDebugInfo)
            {
                // mark the initial jump as hidden. We do it to tell that this is not a part of previous statement. This
                // jump may be a target of another jump (for example if loops are nested) and that would give the
                // impression that the previous statement is being re-executed.
                gotoContinue = new BoundSequencePoint(null, gotoContinue);
            }

            if (!innerLocals.IsDefaultOrEmpty)
            {
                return BoundStatementList.Synthesized(syntax, hasErrors,
                    gotoContinue,
                    new BoundLabelStatement(syntax, startLabel),
                    new BoundBlock(syntax,
                                   innerLocals,
                                   ImmutableArray.Create<BoundStatement>(
                                        rewrittenBody,
                                        new BoundLabelStatement(syntax, continueLabel),
                                        ifConditionGotoStart)),
                    new BoundLabelStatement(syntax, breakLabel));
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                gotoContinue,
                new BoundLabelStatement(syntax, startLabel),
                rewrittenBody,
                new BoundLabelStatement(syntax, continueLabel),
                ifConditionGotoStart,
                new BoundLabelStatement(syntax, breakLabel));
        }
        protected void AddState(out int stateNumber, out GeneratedLabelSymbol resumeLabel)
        {
            stateNumber = nextState++;

            if (dispatches == null)
            {
                dispatches = new Dictionary<LabelSymbol, List<int>>();
            }

            if (this.useFinalizerBookkeeping && !hasFinalizerState)
            {
                currentFinalizerState = nextState++;
                hasFinalizerState = true;
            }

            resumeLabel = F.GenerateLabel("stateMachine");
            List<int> states = new List<int>();
            states.Add(stateNumber);
            dispatches.Add(resumeLabel, states);

            if (this.useFinalizerBookkeeping)
            {
                finalizerStateMap.Add(stateNumber, currentFinalizerState);
            }
        }
Esempio n. 23
0
        private void EmitSwitchBody(
            ImmutableArray<LocalSymbol> locals,
            ImmutableArray<BoundSwitchSection> switchSections,
            GeneratedLabelSymbol breakLabel,
            SyntaxNode syntaxNode)
        {
            var hasLocals = !locals.IsEmpty;

            if (hasLocals)
            {
                _builder.OpenLocalScope();

                foreach (var local in locals)
                {
                    DefineLocal(local, syntaxNode);
                }
            }

            foreach (var section in switchSections)
            {
                EmitSwitchSection(section);
            }

            _builder.MarkLabel(breakLabel);

            if (hasLocals)
            {
                _builder.CloseLocalScope();
            }
        }
        private BoundStatement RewriteForStatement(
            CSharpSyntaxNode syntax,
            ImmutableArray<LocalSymbol> outerLocals,
            BoundStatement rewrittenInitializer,
            BoundExpression rewrittenCondition,
            CSharpSyntaxNode conditionSyntaxOpt,
            TextSpan conditionSpanOpt,
            BoundStatement rewrittenIncrement,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            Debug.Assert(rewrittenBody != null);

            // The sequence point behavior exhibited here is different from that of the native compiler.  In the native
            // compiler, if you have something like 
            //
            // for([|int i = 0, j = 0|]; ; [|i++, j++|])
            //
            // then all the initializers are treated as a single sequence point, as are
            // all the loop incrementors.
            //
            // We now make each one individually a sequence point:
            //
            // for([|int i = 0|], [|j = 0|]; ; [|i++|], [|j++|])
            //
            // If we decide that we want to preserve the native compiler stepping behavior
            // then we'll need to be a bit fancy here. The initializer and increment statements
            // can contain lambdas whose bodies need to have sequence points inserted, so we
            // need to make sure we visit the children. But we'll also need to make sure that
            // we do not generate one sequence point for each statement in the initializers
            // and the incrementors.

            var statementBuilder = ArrayBuilder<BoundStatement>.GetInstance();
            if (rewrittenInitializer != null)
            {
                statementBuilder.Add(rewrittenInitializer);
            }

            var startLabel = new GeneratedLabelSymbol("start");

            // for (initializer; condition; increment)
            //   body;
            //
            // becomes the following (with block added for locals)
            //
            // {
            //   initializer;
            //   goto end;
            // start:
            //   body;
            // continue:
            //   increment;
            // end:
            //   GotoIfTrue condition start;
            // break:
            // }

            var endLabel = new GeneratedLabelSymbol("end");

            //  initializer;
            //  goto end;

            // Mark the initial jump as hidden.
            // We do it to tell that this is not a part of previous statement.
            // This jump may be a target of another jump (for example if loops are nested) and that will make 
            // impression of the previous statement being re-executed
            var gotoEnd = new BoundSequencePoint(null, new BoundGotoStatement(syntax, endLabel));
            statementBuilder.Add(gotoEnd);

            // start:
            //   body;
            statementBuilder.Add(new BoundLabelStatement(syntax, startLabel));

            statementBuilder.Add(rewrittenBody);

            // continue:
            //   increment;
            statementBuilder.Add(new BoundLabelStatement(syntax, continueLabel));
            if (rewrittenIncrement != null)
            {
                statementBuilder.Add(rewrittenIncrement);
            }

            // end:
            //   GotoIfTrue condition start;
            statementBuilder.Add(new BoundLabelStatement(syntax, endLabel));
            BoundStatement branchBack = null;
            if (rewrittenCondition != null)
            {
                branchBack = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, true, startLabel);
            }
            else
            {
                branchBack = new BoundGotoStatement(syntax, startLabel);
            }

            if (this.GenerateDebugInfo)
            {
                if (!conditionSpanOpt.IsEmpty)
                {
                    branchBack = new BoundSequencePointWithSpan(syntax, branchBack, conditionSpanOpt);
                }
                else
                {
                    // hidden sequence point if there is no condition
                    branchBack = new BoundSequencePoint(conditionSyntaxOpt, branchBack);
                }
            }

            statementBuilder.Add(branchBack);

            // break:
            statementBuilder.Add(new BoundLabelStatement(syntax, breakLabel));

            var statements = statementBuilder.ToImmutableAndFree();
            return new BoundBlock(syntax, outerLocals, statements, hasErrors);
        }
        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 LabelSymbol ProxyReturnIfNeeded(
                MethodSymbol containingMethod,
                BoundExpression valueOpt,
                out SynthesizedLocal returnValue)
            {
                returnValue = null;

                // no need to proxy returns  at the root
                if (this.IsRoot())
                {
                    return null;
                }

                var returnProxy = this.returnProxyLabel;
                if (returnProxy == null)
                {
                    this.returnProxyLabel = returnProxy = new GeneratedLabelSymbol("returnProxy");
                }

                if (valueOpt != null)
                {
                    returnValue = this.returnValue;
                    if (returnValue == null)
                    {
                        Debug.Assert(_tryStatementSyntaxOpt != null);
                        this.returnValue = returnValue = new SynthesizedLocal(containingMethod, valueOpt.Type, SynthesizedLocalKind.AsyncMethodReturnValue, _tryStatementSyntaxOpt);
                    }
                }

                return returnProxy;
            }
Esempio n. 27
0
        public BoundStatement If(BoundExpression condition, ImmutableArray<LocalSymbol> locals, BoundStatement thenClause, BoundStatement elseClauseOpt = null)
        {
            // We translate
            //    if (condition) thenClause else elseClause
            // as
            //    {
            //       ConditionalGoto(!condition) alternative
            //       thenClause
            //       goto afterif;
            //       alternative:
            //       elseClause
            //       afterif:
            //    }
            Debug.Assert(thenClause != null);

            var statements = ArrayBuilder<BoundStatement>.GetInstance();
            var afterif = new GeneratedLabelSymbol("afterif");

            if (elseClauseOpt != null)
            {
                var alt = new GeneratedLabelSymbol("alternative");

                statements.Add(new BoundConditionalGoto(Syntax, condition, false, alt) { WasCompilerGenerated = true });
                statements.Add(thenClause);
                statements.Add(Goto(afterif));
                if (!locals.IsDefaultOrEmpty)
                {
                    var firstPart = this.Block(locals, statements.ToImmutable());
                    statements.Clear();
                    statements.Add(firstPart);
                }

                statements.Add(Label(alt));
                statements.Add(elseClauseOpt);
            }
            else
            {
                statements.Add(new BoundConditionalGoto(Syntax, condition, false, afterif) { WasCompilerGenerated = true });
                statements.Add(thenClause);
                if (!locals.IsDefaultOrEmpty)
                {
                    var firstPart = this.Block(locals, statements.ToImmutable());
                    statements.Clear();
                    statements.Add(firstPart);
                }
            }

            statements.Add(Label(afterif));
            return Block(statements.ToImmutableAndFree());
        }
 internal SwitchBinder(Binder next, SwitchStatementSyntax switchSyntax)
     : base(next)
 {
     this.switchSyntax = switchSyntax;
     this.breakLabel = new GeneratedLabelSymbol("break");
 }
            // 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;
            }
        /// <summary>
        /// Lower a foreach loop that will enumerate a multi-dimensional array.
        /// 
        /// A[...] a = x;
        /// int q_0 = a.GetUpperBound(0), q_1 = a.GetUpperBound(1), ...;
        /// for (int p_0 = a.GetLowerBound(0); p_0 &lt;= q_0; p_0 = p_0 + 1)
        ///     for (int p_1 = a.GetLowerBound(1); p_1 &lt;= q_1; p_1 = p_1 + 1)
        ///         ...
        ///             { V v = (V)a[p_0, p_1, ...]; /* body */ }
        /// </summary>
        /// <remarks>
        /// We will follow Dev10 in diverging from the C# 4 spec by ignoring Array's 
        /// implementation of IEnumerable and just indexing into its elements.
        /// 
        /// NOTE: We're assuming that sequence points have already been generated.
        /// Otherwise, lowering to nested for-loops would generated spurious ones.
        /// </remarks>
        private BoundStatement RewriteMultiDimensionalArrayForEachStatement(BoundForEachStatement node)
        {
            ForEachStatementSyntax forEachSyntax = (ForEachStatementSyntax)node.Syntax;

            BoundExpression collectionExpression = GetUnconvertedCollectionExpression(node);
            Debug.Assert(collectionExpression.Type.IsArray());

            ArrayTypeSymbol arrayType = (ArrayTypeSymbol)collectionExpression.Type;

            int rank = arrayType.Rank;
            Debug.Assert(!arrayType.IsSZArray);

            TypeSymbol intType = _compilation.GetSpecialType(SpecialType.System_Int32);
            TypeSymbol boolType = _compilation.GetSpecialType(SpecialType.System_Boolean);

            // Values we'll use every iteration
            MethodSymbol getLowerBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetLowerBound);
            MethodSymbol getUpperBoundMethod = GetSpecialTypeMethod(forEachSyntax, SpecialMember.System_Array__GetUpperBound);

            BoundExpression rewrittenExpression = (BoundExpression)Visit(collectionExpression);
            BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body);

            // A[...] a
            LocalSymbol arrayVar = _factory.SynthesizedLocal(arrayType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArray);
            BoundLocal boundArrayVar = MakeBoundLocal(forEachSyntax, arrayVar, arrayType);

            // A[...] a = /*node.Expression*/;
            BoundStatement arrayVarDecl = MakeLocalDeclaration(forEachSyntax, arrayVar, rewrittenExpression);

            AddForEachExpressionSequencePoint(forEachSyntax, ref arrayVarDecl);

            // NOTE: dev10 initializes all of the upper bound temps before entering the loop (as opposed to
            // initializing each one at the corresponding level of nesting).  Doing it at the same time as
            // the lower bound would make this code a bit simpler, but it would make it harder to compare
            // the roslyn and dev10 IL.

            // int q_0, q_1, ...
            LocalSymbol[] upperVar = new LocalSymbol[rank];
            BoundLocal[] boundUpperVar = new BoundLocal[rank];
            BoundStatement[] upperVarDecl = new BoundStatement[rank];
            for (int dimension = 0; dimension < rank; dimension++)
            {
                // int q_dimension
                upperVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayLimit);
                boundUpperVar[dimension] = MakeBoundLocal(forEachSyntax, upperVar[dimension], intType);

                ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create(
                    MakeLiteral(forEachSyntax,
                        constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32),
                        type: intType));

                // a.GetUpperBound(dimension)
                BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument);

                // int q_dimension = a.GetUpperBound(dimension);
                upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound);
            }

            // int p_0, p_1, ...
            LocalSymbol[] positionVar = new LocalSymbol[rank];
            BoundLocal[] boundPositionVar = new BoundLocal[rank];
            for (int dimension = 0; dimension < rank; dimension++)
            {
                positionVar[dimension] = _factory.SynthesizedLocal(intType, syntax: forEachSyntax, kind: SynthesizedLocalKind.ForEachArrayIndex);
                boundPositionVar[dimension] = MakeBoundLocal(forEachSyntax, positionVar[dimension], intType);
            }

            // V v
            LocalSymbol iterationVar = node.IterationVariable;
            TypeSymbol iterationVarType = iterationVar.Type;

            // (V)a[p_0, p_1, ...]
            BoundExpression iterationVarInitValue = MakeConversion(
                syntax: forEachSyntax,
                rewrittenOperand: new BoundArrayAccess(forEachSyntax,
                    expression: boundArrayVar,
                    indices: ImmutableArray.Create((BoundExpression[])boundPositionVar),
                    type: arrayType.ElementType),
                conversion: node.ElementConversion,
                rewrittenType: iterationVarType,
                @checked: node.Checked);

            // V v = (V)a[p_0, p_1, ...];
            BoundStatement iterationVarDecl = MakeLocalDeclaration(forEachSyntax, iterationVar, iterationVarInitValue);

            AddForEachIterationVariableSequencePoint(forEachSyntax, ref iterationVarDecl);

            // { V v = (V)a[p_0, p_1, ...]; /* node.Body */ }

            BoundStatement innermostLoopBody = CreateBlockDeclaringIterationVariable(iterationVar, iterationVarDecl, rewrittenBody, forEachSyntax);

            // work from most-nested to least-nested
            // for (int p_0 = a.GetLowerBound(0); p_0 <= q_0; p_0 = p_0 + 1)
            //     for (int p_1 = a.GetLowerBound(0); p_1 <= q_1; p_1 = p_1 + 1)
            //         ...
            //             { V v = (V)a[p_0, p_1, ...]; /* node.Body */ }
            BoundStatement forLoop = null;
            for (int dimension = rank - 1; dimension >= 0; dimension--)
            {
                ImmutableArray<BoundExpression> dimensionArgument = ImmutableArray.Create(
                    MakeLiteral(forEachSyntax,
                        constantValue: ConstantValue.Create(dimension, ConstantValueTypeDiscriminator.Int32),
                        type: intType));

                // a.GetLowerBound(dimension)
                BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument);

                // int p_dimension = a.GetLowerBound(dimension);
                BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound);

                GeneratedLabelSymbol breakLabel = dimension == 0 // outermost for-loop
                    ? node.BreakLabel // i.e. the one that break statements will jump to
                    : new GeneratedLabelSymbol("break"); // Should not affect emitted code since unused

                // p_dimension <= q_dimension  //NB: OrEqual
                BoundExpression exitCondition = new BoundBinaryOperator(
                    syntax: forEachSyntax,
                    operatorKind: BinaryOperatorKind.IntLessThanOrEqual,
                    left: boundPositionVar[dimension],
                    right: boundUpperVar[dimension],
                    constantValueOpt: null,
                    methodOpt: null,
                    resultKind: LookupResultKind.Viable,
                    type: boolType);

                // p_dimension = p_dimension + 1;
                BoundStatement positionIncrement = MakePositionIncrement(forEachSyntax, boundPositionVar[dimension], intType);

                BoundStatement body;
                GeneratedLabelSymbol continueLabel;

                if (forLoop == null)
                {
                    // innermost for-loop
                    body = innermostLoopBody;
                    continueLabel = node.ContinueLabel; //i.e. the one continue statements will actually jump to
                }
                else
                {
                    body = forLoop;
                    continueLabel = new GeneratedLabelSymbol("continue"); // Should not affect emitted code since unused
                }

                forLoop = RewriteForStatement(
                    syntax: forEachSyntax,
                    outerLocals: ImmutableArray.Create(positionVar[dimension]),
                    rewrittenInitializer: positionVarDecl,
                    rewrittenCondition: exitCondition,
                    conditionSyntaxOpt: null,
                    conditionSpanOpt: forEachSyntax.InKeyword.Span,
                    rewrittenIncrement: positionIncrement,
                    rewrittenBody: body,
                    breakLabel: breakLabel,
                    continueLabel: continueLabel,
                    hasErrors: node.HasErrors);
            }

            Debug.Assert(forLoop != null);

            BoundStatement result = new BoundBlock(
                forEachSyntax,
                ImmutableArray.Create(arrayVar).Concat(upperVar.AsImmutableOrNull()),
                ImmutableArray<LocalFunctionSymbol>.Empty,
                ImmutableArray.Create(arrayVarDecl).Concat(upperVarDecl.AsImmutableOrNull()).Add(forLoop));

            AddForEachKeywordSequencePoint(forEachSyntax, ref result);

            return result;
        }