예제 #1
0
        /// <summary>
        /// Construct a body for an auto-property accessor (updating or returning the backing field).
        /// </summary>
        internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMethodSymbol accessor)
        {
            Debug.Assert(accessor.MethodKind == MethodKind.PropertyGet || accessor.MethodKind == MethodKind.PropertySet);

            var property                   = (SourcePropertySymbol)accessor.AssociatedSymbol;
            CSharpSyntaxNode syntax        = property.CSharpSyntaxNode;
            BoundExpression  thisReference = null;

            if (!accessor.IsStatic)
            {
                var thisSymbol = accessor.ThisParameter;
                thisReference = new BoundThisReference(syntax, thisSymbol.Type)
                {
                    WasCompilerGenerated = true
                };
            }

            var field       = property.BackingField;
            var fieldAccess = new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable)
            {
                WasCompilerGenerated = true
            };
            BoundStatement statement;

            if (accessor.MethodKind == MethodKind.PropertyGet)
            {
                statement = new BoundReturnStatement(syntax, RefKind.None, fieldAccess)
                {
                    WasCompilerGenerated = true
                };
            }
            else
            {
                Debug.Assert(accessor.MethodKind == MethodKind.PropertySet);
                var parameter = accessor.Parameters[0];
                statement = new BoundExpressionStatement(
                    syntax,
                    new BoundAssignmentOperator(
                        syntax,
                        fieldAccess,
                        new BoundParameter(syntax, parameter)
                {
                    WasCompilerGenerated = true
                },
                        property.Type)
                {
                    WasCompilerGenerated = true
                })
                {
                    WasCompilerGenerated = true
                };
            }

            statement = new BoundSequencePoint(accessor.SyntaxNode, statement)
            {
                WasCompilerGenerated = true
            };

            return(BoundBlock.SynthesizedNoLocals(syntax, statement));
        }
        public override BoundNode VisitReturnStatement(BoundReturnStatement node)
        {
            BoundStatement rewritten = (BoundStatement)base.VisitReturnStatement(node);

            // NOTE: we will apply sequence points to synthesized return
            // statements if they are contained in lambdas and have expressions
            // or if they are expression-bodied properties.
            // We do this to ensure that expression lambdas and expression-bodied
            // properties have sequence points.
            // We also add sequence points for the implicit "return" statement at the end of the method body
            // (added by FlowAnalysisPass.AppendImplicitReturn). Implicitly added return for async method
            // does not need sequence points added here since it would be done later (presumably during Async rewrite).
            if (this.Instrument &&
                (!node.WasCompilerGenerated ||
                 (node.ExpressionOpt != null ?
                  IsLambdaOrExpressionBodiedMember :
                  (node.Syntax.Kind() == SyntaxKind.Block && _factory.CurrentMethod?.IsAsync == false))))
            {
                rewritten = _instrumenter.InstrumentReturnStatement(node, rewritten);
            }
            else if (node.WasCompilerGenerated && _factory.CurrentMethod?.IsAsync == true)
            {
                rewritten = new BoundSequencePoint(null, rewritten);
            }

            return(rewritten);
        }
예제 #3
0
        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)));
        }
        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));
        }
예제 #5
0
        private BoundStatement AddSequencePoint(BoundStatement node)
        {
            if (this.GenerateDebugInfo && !node.WasCompilerGenerated)
            {
                node = new BoundSequencePoint(node.Syntax, node);
            }

            return(node);
        }
 /// <summary>
 /// Add sequence point |here|:
 ///
 /// foreach (Type var in |expr|) { }
 /// </summary>
 /// <remarks>
 /// Hit once, before looping begins.
 /// </remarks>
 private void AddForEachExpressionSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement collectionVarDecl)
 {
     if (this.GenerateDebugInfo)
     {
         // NOTE: This is slightly different from Dev10.  In Dev10, when you stop the debugger
         // on the collection expression, you can see the (uninitialized) iteration variable.
         // In Roslyn, you cannot because the iteration variable is re-declared in each iteration
         // of the loop and is, therefore, not yet in scope.
         collectionVarDecl = new BoundSequencePoint(forEachSyntax.Expression, collectionVarDecl);
     }
 }
        internal static BoundStatement AddSequencePoint(VariableDeclaratorSyntax declaratorSyntax, BoundStatement rewrittenStatement)
        {
            SyntaxNode node;
            TextSpan?  part;

            GetBreakpointSpan(declaratorSyntax, out node, out part);
            var result = BoundSequencePoint.Create(declaratorSyntax, part, rewrittenStatement);

            result.WasCompilerGenerated = rewrittenStatement.WasCompilerGenerated;
            return(result);
        }
        internal static BoundStatement AddSequencePoint(PropertyDeclarationSyntax declarationSyntax, BoundStatement rewrittenStatement)
        {
            Debug.Assert(declarationSyntax.Initializer != null);
            int      start = declarationSyntax.Initializer.Value.SpanStart;
            int      end   = declarationSyntax.Initializer.Span.End;
            TextSpan part  = TextSpan.FromBounds(start, end);

            var result = BoundSequencePoint.Create(declarationSyntax, part, rewrittenStatement);

            result.WasCompilerGenerated = rewrittenStatement.WasCompilerGenerated;
            return(result);
        }
예제 #9
0
        internal static BoundStatement AddSequencePoint(PropertyDeclarationSyntax declarationSyntax, BoundStatement rewrittenStatement)
        {
            SyntaxTokenList modifiers = declarationSyntax.Modifiers;
            // Skip attributes
            int      start = modifiers.Any() ? modifiers[0].SpanStart : declarationSyntax.Type.SpanStart;
            int      end   = declarationSyntax.Span.End;
            TextSpan part  = TextSpan.FromBounds(start, end);

            var result = BoundSequencePoint.Create(declarationSyntax, part, rewrittenStatement);

            result.WasCompilerGenerated = rewrittenStatement.WasCompilerGenerated;
            return(result);
        }
예제 #10
0
        private BoundStatement RewriteWhileStatement(
            BoundWhileStatement loop,
            ImmutableArray <LocalSymbol> locals,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            if (locals.IsEmpty)
            {
                return(RewriteWhileStatement(loop, rewrittenCondition, rewrittenBody, breakLabel, continueLabel, hasErrors));
            }

            // We need to enter 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.

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

            SyntaxNode     syntax = loop.Syntax;
            BoundStatement continueLabelStatement  = new BoundLabelStatement(syntax, continueLabel);
            BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

            if (this.Instrument && !loop.WasCompilerGenerated)
            {
                ifNotConditionGotoBreak = _instrumenter.InstrumentWhileStatementConditionalGotoStartOrBreak(loop, ifNotConditionGotoBreak);
                continueLabelStatement  = new BoundSequencePoint(null, continueLabelStatement);
            }

            return(BoundStatementList.Synthesized(syntax, hasErrors,
                                                  continueLabelStatement,
                                                  new BoundBlock(syntax,
                                                                 locals,
                                                                 ImmutableArray.Create(
                                                                     ifNotConditionGotoBreak,
                                                                     rewrittenBody,
                                                                     new BoundGotoStatement(syntax, continueLabel))),
                                                  new BoundLabelStatement(syntax, breakLabel)));
        }
        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)));
        }
예제 #12
0
        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 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));
        }
        /// <summary>
        /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
        /// before and after the body, respectively.
        /// 
        /// C# 4.0 version
        /// 
        /// L locked;
        /// bool flag = false;
        /// try {
        ///     locked = `argument`;
        ///     Monitor.Enter(locked, ref flag);
        ///     `body`
        /// } finally {
        ///     if (flag) Monitor.Exit(locked);
        /// }
        ///
        /// Pre-4.0 version
        /// 
        /// L locked = `argument`;
        /// Monitor.Enter(locked, ref flag);
        /// try {
        ///     `body`
        /// } finally {
        ///     Monitor.Exit(locked);
        /// }
        /// </summary>
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax;

            BoundExpression rewrittenArgument = VisitExpression(node.Argument);
            BoundStatement rewrittenBody = (BoundStatement)Visit(node.Body);

            TypeSymbol argumentType = rewrittenArgument.Type;
            if ((object)argumentType == null)
            {
                // This isn't particularly elegant, but hopefully locking on null is
                // not very common.
                Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null);
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);
                rewrittenArgument = MakeLiteral(
                    rewrittenArgument.Syntax,
                    rewrittenArgument.ConstantValue,
                    argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
            }
            if (argumentType.Kind == SymbolKind.TypeParameter)
            {
                // If the argument has a type parameter type, then we'll box it right away
                // so that the same object is passed to both Monitor.Enter and Monitor.Exit.
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);

                rewrittenArgument = MakeConversion(
                    rewrittenArgument.Syntax,
                    rewrittenArgument,
                    ConversionKind.Boxing,
                    argumentType,
                    @checked: false,
                    constantValueOpt: rewrittenArgument.ConstantValue);
            }

            BoundAssignmentOperator assignmentToLockTemp;
            BoundLocal boundLockTemp = this.factory.StoreToTemp(rewrittenArgument, out assignmentToLockTemp, kind: SynthesizedLocalKind.Lock);

            BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);
            if (this.GenerateDebugInfo)
            {
                boundLockTempInit = new BoundSequencePointWithSpan( // NOTE: the lock temp is uninitialized at this sequence point.
                    lockSyntax,
                    boundLockTempInit,
                    TextSpan.FromBounds(lockSyntax.LockKeyword.SpanStart, lockSyntax.CloseParenToken.Span.End));
            }

            BoundExpression exitCallExpr;

            MethodSymbol exitMethod;
            if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
            {
                exitCallExpr = BoundCall.Synthesized(
                    lockSyntax,
                    null,
                    exitMethod,
                    boundLockTemp);
            }
            else
            {
                exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
            }

            BoundStatement exitCall = new BoundExpressionStatement(
                lockSyntax,
                exitCallExpr);

            MethodSymbol enterMethod;

            if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
                 TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one. 
                enterMethod.ParameterCount == 2)
            {
                // C# 4.0 version
                // L locked;
                // bool flag = false;
                // try {
                //     locked = `argument`;
                //     Monitor.Enter(locked, ref flag);
                //     `body`
                // } finally {
                //     if (flag) Monitor.Exit(locked);
                // }

                TypeSymbol boolType = this.compilation.GetSpecialType(SpecialType.System_Boolean);
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundFlagTemp = this.factory.StoreToTemp(
                    MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType),
                    kind: SynthesizedLocalKind.LockTaken,
                    store: out assignmentToTemp);

                BoundStatement boundFlagTempInit = new BoundExpressionStatement(lockSyntax, assignmentToTemp);
                if (this.GenerateDebugInfo)
                {
                    // hide the preamble code, we should not stop until we get to " locked = `argument`; "
                    boundFlagTempInit = new BoundSequencePoint(null, boundFlagTempInit);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp,
                        boundFlagTemp));

                exitCall = RewriteIfStatement(
                    lockSyntax,
                    ImmutableArray<LocalSymbol>.Empty,
                    boundFlagTemp,
                    exitCall,
                    null,
                    node.HasErrors);

                return new BoundBlock(
                    lockSyntax,
                    node.Locals.Concat(ImmutableArray.Create<LocalSymbol>(boundLockTemp.LocalSymbol, boundFlagTemp.LocalSymbol)),
                    ImmutableArray.Create<BoundStatement>(
                        boundFlagTempInit,
                        new BoundTryStatement(
                            lockSyntax,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, boundLockTempInit, enterCall, rewrittenBody),
                            ImmutableArray<BoundCatchBlock>.Empty,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))));
            }
            else
            {
                BoundExpression enterCallExpr;

                if ((object)enterMethod != null)
                {
                    Debug.Assert(enterMethod.ParameterCount == 1);
                    // Pre-4.0 version
                    // L locked = `argument`;
                    // Monitor.Enter(locked, ref flag); //NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock.
                    // try {
                    //     `body`
                    // } finally {
                    //     Monitor.Exit(locked);
                    // }

                    enterCallExpr = BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp);
                }
                else
                {
                    enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray<Symbol>.Empty, ImmutableArray.Create<BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    enterCallExpr);

                return new BoundBlock(
                    lockSyntax,
                    node.Locals.Add(boundLockTemp.LocalSymbol),
                    ImmutableArray.Create<BoundStatement>(
                        boundLockTempInit,
                        enterCall,
                        new BoundTryStatement(
                            lockSyntax,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody),
                            ImmutableArray<BoundCatchBlock>.Empty,
                            BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall))));
            }
        }
        private BoundStatement RewriteWhileStatement(
            BoundWhileStatement loop,
            ImmutableArray<LocalSymbol> locals,
            BoundExpression rewrittenCondition,
            BoundStatement rewrittenBody,
            GeneratedLabelSymbol breakLabel,
            GeneratedLabelSymbol continueLabel,
            bool hasErrors)
        {
            if (locals.IsEmpty)
            {
                return RewriteWhileStatement(loop, rewrittenCondition, rewrittenBody, breakLabel, continueLabel, hasErrors);
            }

            // We need to enter 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.

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

            SyntaxNode syntax = loop.Syntax;
            BoundStatement continueLabelStatement = new BoundLabelStatement(syntax, continueLabel);
            BoundStatement ifNotConditionGotoBreak = new BoundConditionalGoto(rewrittenCondition.Syntax, rewrittenCondition, false, breakLabel);

            if (this.Instrument && !loop.WasCompilerGenerated)
            {
                ifNotConditionGotoBreak = _instrumenter.InstrumentWhileStatementConditionalGotoStartOrBreak(loop, ifNotConditionGotoBreak);
                continueLabelStatement = new BoundSequencePoint(null, continueLabelStatement);
            }

            return BoundStatementList.Synthesized(syntax, hasErrors,
                continueLabelStatement,
                new BoundBlock(syntax,
                               locals,
                               ImmutableArray.Create(
                                    ifNotConditionGotoBreak,
                                    rewrittenBody,
                                    new BoundGotoStatement(syntax, continueLabel))),
                new BoundLabelStatement(syntax, breakLabel));
        }
        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);
        }
        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 <BoundStatement>(
                                                                             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)));
        }
        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 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);
        }
예제 #20
0
        /// <summary>
        /// Lowers a lock statement to a try-finally block that calls Monitor.Enter and Monitor.Exit
        /// before and after the body, respectively.
        ///
        /// C# 4.0 version
        ///
        /// L locked;
        /// bool flag = false;
        /// try {
        ///     locked = `argument`;
        ///     Monitor.Enter(locked, ref flag);
        ///     `body`
        /// } finally {
        ///     if (flag) Monitor.Exit(locked);
        /// }
        ///
        /// Pre-4.0 version
        ///
        /// L locked = `argument`;
        /// Monitor.Enter(locked, ref flag);
        /// try {
        ///     `body`
        /// } finally {
        ///     Monitor.Exit(locked);
        /// }
        /// </summary>
        public override BoundNode VisitLockStatement(BoundLockStatement node)
        {
            LockStatementSyntax lockSyntax = (LockStatementSyntax)node.Syntax;

            BoundExpression rewrittenArgument = VisitExpression(node.Argument);
            BoundStatement  rewrittenBody     = (BoundStatement)Visit(node.Body);

            TypeSymbol argumentType = rewrittenArgument.Type;

            if ((object)argumentType == null)
            {
                // This isn't particularly elegant, but hopefully locking on null is
                // not very common.
                Debug.Assert(rewrittenArgument.ConstantValue == ConstantValue.Null);
                argumentType      = this.compilation.GetSpecialType(SpecialType.System_Object);
                rewrittenArgument = MakeLiteral(
                    rewrittenArgument.Syntax,
                    rewrittenArgument.ConstantValue,
                    argumentType); //need to have a non-null type here for TempHelpers.StoreToTemp.
            }
            if (argumentType.Kind == SymbolKind.TypeParameter)
            {
                // If the argument has a type parameter type, then we'll box it right away
                // so that the same object is passed to both Monitor.Enter and Monitor.Exit.
                argumentType = this.compilation.GetSpecialType(SpecialType.System_Object);

                rewrittenArgument = MakeConversion(
                    rewrittenArgument.Syntax,
                    rewrittenArgument,
                    ConversionKind.Boxing,
                    argumentType,
                    @checked: false,
                    constantValueOpt: rewrittenArgument.ConstantValue);
            }

            BoundAssignmentOperator assignmentToLockTemp;
            BoundLocal boundLockTemp = this.factory.StoreToTemp(rewrittenArgument, tempKind: TempKind.Lock, store: out assignmentToLockTemp);

            BoundStatement boundLockTempInit = new BoundExpressionStatement(lockSyntax, assignmentToLockTemp);

            if (this.generateDebugInfo)
            {
                boundLockTempInit = new BoundSequencePointWithSpan( // NOTE: the lock temp is uninitialized at this sequence point.
                    lockSyntax,
                    boundLockTempInit,
                    TextSpan.FromBounds(lockSyntax.LockKeyword.SpanStart, lockSyntax.CloseParenToken.Span.End));
            }

            BoundExpression exitCallExpr;

            MethodSymbol exitMethod;

            if (TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Exit, out exitMethod))
            {
                exitCallExpr = BoundCall.Synthesized(
                    lockSyntax,
                    null,
                    exitMethod,
                    boundLockTemp);
            }
            else
            {
                exitCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
            }

            BoundStatement exitCall = new BoundExpressionStatement(
                lockSyntax,
                exitCallExpr);

            MethodSymbol enterMethod;

            if ((TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter2, out enterMethod, isOptional: true) ||
                 TryGetWellKnownTypeMember(lockSyntax, WellKnownMember.System_Threading_Monitor__Enter, out enterMethod)) && // If we didn't find the overload introduced in .NET 4.0, then use the older one.
                enterMethod.ParameterCount == 2)
            {
                // C# 4.0 version
                // L locked;
                // bool flag = false;
                // try {
                //     locked = `argument`;
                //     Monitor.Enter(locked, ref flag);
                //     `body`
                // } finally {
                //     if (flag) Monitor.Exit(locked);
                // }

                TypeSymbol boolType = this.compilation.GetSpecialType(SpecialType.System_Boolean);
                BoundAssignmentOperator assignmentToTemp;
                BoundLocal boundFlagTemp = this.factory.StoreToTemp(
                    MakeLiteral(rewrittenArgument.Syntax, ConstantValue.False, boolType),
                    tempKind: TempKind.LockTaken,
                    store: out assignmentToTemp);

                BoundStatement boundFlagTempInit = new BoundExpressionStatement(lockSyntax, assignmentToTemp);
                if (this.generateDebugInfo)
                {
                    // hide the preamble code, we should not stop until we get to " locked = `argument`; "
                    boundFlagTempInit = new BoundSequencePoint(null, boundFlagTempInit);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp,
                        boundFlagTemp));

                exitCall = RewriteIfStatement(
                    lockSyntax,
                    boundFlagTemp,
                    exitCall,
                    null,
                    node.HasErrors);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol, boundFlagTemp.LocalSymbol),
                           ImmutableArray.Create <BoundStatement>(
                               boundFlagTempInit,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, boundLockTempInit, enterCall, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
            else
            {
                BoundExpression enterCallExpr;

                if ((object)enterMethod != null)
                {
                    Debug.Assert(enterMethod.ParameterCount == 1);
                    // Pre-4.0 version
                    // L locked = `argument`;
                    // Monitor.Enter(locked, ref flag); //NB: before try-finally so we don't Exit if an exception prevents us from acquiring the lock.
                    // try {
                    //     `body`
                    // } finally {
                    //     Monitor.Exit(locked);
                    // }

                    enterCallExpr = BoundCall.Synthesized(
                        lockSyntax,
                        null,
                        enterMethod,
                        boundLockTemp);
                }
                else
                {
                    enterCallExpr = new BoundBadExpression(lockSyntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundNode>(boundLockTemp), ErrorTypeSymbol.UnknownResultType);
                }

                BoundStatement enterCall = new BoundExpressionStatement(
                    lockSyntax,
                    enterCallExpr);

                return(new BoundBlock(
                           lockSyntax,
                           ImmutableArray.Create <LocalSymbol>(boundLockTemp.LocalSymbol),
                           ImmutableArray.Create <BoundStatement>(
                               boundLockTempInit,
                               enterCall,
                               new BoundTryStatement(
                                   lockSyntax,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, rewrittenBody),
                                   ImmutableArray <BoundCatchBlock> .Empty,
                                   BoundBlock.SynthesizedNoLocals(lockSyntax, exitCall)))));
            }
        }
 /// <summary>
 /// Add sequence point |here|:
 /// 
 /// foreach (Type var in |expr|) { }
 /// </summary>
 /// <remarks>
 /// Hit once, before looping begins.
 /// </remarks>
 private void AddForEachExpressionSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement collectionVarDecl)
 {
     if (this.GenerateDebugInfo)
     {
         // NOTE: This is slightly different from Dev10.  In Dev10, when you stop the debugger
         // on the collection expression, you can see the (uninitialized) iteration variable.
         // In Roslyn, you cannot because the iteration variable is re-declared in each iteration
         // of the loop and is, therefore, not yet in scope.
         collectionVarDecl = new BoundSequencePoint(forEachSyntax.Expression, collectionVarDecl);
     }
 }
        /// <summary>
        /// Construct a body for an auto-property accessor (updating or returning the backing field).
        /// </summary>
        internal static BoundBlock ConstructAutoPropertyAccessorBody(SourceMethodSymbol accessor)
        {
            Debug.Assert(accessor.MethodKind == MethodKind.PropertyGet || accessor.MethodKind == MethodKind.PropertySet);

            var property = (SourcePropertySymbol)accessor.AssociatedSymbol;
            CSharpSyntaxNode syntax = property.CSharpSyntaxNode;
            BoundExpression thisReference = null;
            if (!accessor.IsStatic)
            {
                var thisSymbol = accessor.ThisParameter;
                thisReference = new BoundThisReference(syntax, thisSymbol.Type) { WasCompilerGenerated = true };
            }

            var field = property.BackingField;
            var fieldAccess = new BoundFieldAccess(syntax, thisReference, field, ConstantValue.NotAvailable) { WasCompilerGenerated = true };
            BoundStatement statement;

            if (accessor.MethodKind == MethodKind.PropertyGet)
            {
                statement = new BoundReturnStatement(syntax, fieldAccess) { WasCompilerGenerated = true };
            }
            else
            {
                Debug.Assert(accessor.MethodKind == MethodKind.PropertySet);
                var parameter = accessor.Parameters[0];
                statement = new BoundExpressionStatement(
                    syntax,
                    new BoundAssignmentOperator(
                        syntax,
                        fieldAccess,
                        new BoundParameter(syntax, parameter) { WasCompilerGenerated = true },
                        property.Type)
                { WasCompilerGenerated = true })
                { WasCompilerGenerated = true };
            }

            statement = new BoundSequencePoint(accessor.SyntaxNode, statement) { WasCompilerGenerated = true };

            return new BoundBlock(syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create<BoundStatement>(statement)) { WasCompilerGenerated = true };
        }
예제 #23
0
        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 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");

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