// insert the implicit "return" statement at the end of the method body // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these // ones are going to have sequence points. internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method = null, CSharpSyntaxNode syntax = null) { if (syntax == null) { syntax = node.Syntax; } BoundStatement ret = (object)method != null && (object)method.IteratorElementType != null ? BoundYieldBreakStatement.Synthesized(syntax) as BoundStatement : BoundReturnStatement.Synthesized(syntax, null); if (syntax.Kind == SyntaxKind.Block) { var blockSyntax = (BlockSyntax)syntax; ret = new BoundSequencePointWithSpan( blockSyntax, ret, blockSyntax.CloseBraceToken.Span) { WasCompilerGenerated = true }; } switch (node.Kind) { case BoundKind.Block: var block = (BoundBlock)node; return block.Update(block.LocalsOpt, block.Statements.Add(ret)); default: return new BoundBlock(syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create(ret, node)); } }
// insert the implicit "return" statement at the end of the method body // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these // ones are going to have sequence points. internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method = null, CSharpSyntaxNode syntax = null) { if (syntax == null) { syntax = node.Syntax; } BoundStatement ret = (object)method != null && (object)method.IteratorElementType != null ? BoundYieldBreakStatement.Synthesized(syntax) as BoundStatement : BoundReturnStatement.Synthesized(syntax, null); if (syntax.Kind == SyntaxKind.Block) { var blockSyntax = (BlockSyntax)syntax; ret = new BoundSequencePointWithSpan( blockSyntax, ret, blockSyntax.CloseBraceToken.Span) { WasCompilerGenerated = true }; } switch (node.Kind) { case BoundKind.Block: var block = (BoundBlock)node; return(block.Update(block.LocalsOpt, block.Statements.Add(ret))); default: return(new BoundBlock(syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create(ret, node))); } }
public override BoundNode VisitIfStatement(BoundIfStatement node) { Debug.Assert(node != null); var rewrittenCondition = VisitExpression(node.Condition); var rewrittenConsequence = VisitStatement(node.Consequence); var rewrittenAlternative = VisitStatement(node.AlternativeOpt); var syntax = (IfStatementSyntax)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. var result = RewriteIfStatement(syntax, node.Locals, AddConditionSequencePoint(rewrittenCondition, node), rewrittenConsequence, rewrittenAlternative, node.HasErrors); // add sequence point before the whole statement if (this.GenerateDebugInfo && !node.WasCompilerGenerated) { result = new BoundSequencePointWithSpan( syntax, result, TextSpan.FromBounds( syntax.IfKeyword.SpanStart, syntax.CloseParenToken.Span.End), node.HasErrors); } return(result); }
// insert the implicit "return" statement at the end of the method body // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these // ones are going to have sequence points. internal static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, CSharpSyntaxNode syntax = null) { Debug.Assert(body != null); Debug.Assert(method != null); if (syntax == null) { syntax = body.Syntax; } Debug.Assert(body.WasCompilerGenerated || syntax.IsKind(SyntaxKind.Block) || syntax.IsKind(SyntaxKind.ArrowExpressionClause)); BoundStatement ret = method.IsIterator ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax) : BoundReturnStatement.Synthesized(syntax, null); // Implicitly added return for async method does not need sequence points since lowering would add one. if (syntax.IsKind(SyntaxKind.Block) && !method.IsAsync) { var blockSyntax = (BlockSyntax)syntax; ret = new BoundSequencePointWithSpan( blockSyntax, ret, blockSyntax.CloseBraceToken.Span) { WasCompilerGenerated = true }; } return(body.Update(body.Locals, body.Statements.Add(ret))); }
public override BoundNode VisitIfStatement(BoundIfStatement node) { Debug.Assert(node != null); var rewrittenCondition = VisitExpression(node.Condition); var rewrittenConsequence = VisitStatement(node.Consequence); var rewrittenAlternative = VisitStatement(node.AlternativeOpt); var syntax = (IfStatementSyntax)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. var result = RewriteIfStatement(syntax, AddConditionSequencePoint(rewrittenCondition, node), rewrittenConsequence, rewrittenAlternative, node.HasErrors); // add sequence point before the whole statement if (this.GenerateDebugInfo && !node.WasCompilerGenerated) { result = new BoundSequencePointWithSpan( syntax, result, TextSpan.FromBounds( syntax.IfKeyword.SpanStart, syntax.CloseParenToken.Span.End), node.HasErrors); } return result; }
internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlock block) { var syntax = block.Syntax; Debug.Assert(method.MethodKind == MethodKind.Destructor); Debug.Assert(syntax.Kind() == SyntaxKind.Block); // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact // requirements), then we need to call that method in a finally block. Otherwise, just return block as-is. // NOTE: the Finalize method need not be a destructor or be overridden by the current method. MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method); if ((object)baseTypeFinalize != null) { BoundStatement baseFinalizeCall = new BoundSequencePointWithSpan( //sequence point to mimic Dev10 syntax, new BoundExpressionStatement( syntax, BoundCall.Synthesized( syntax, new BoundBaseReference( syntax, method.ContainingType) { WasCompilerGenerated = true }, baseTypeFinalize) ) { WasCompilerGenerated = true }, ((BlockSyntax)syntax).CloseBraceToken.Span); return(new BoundBlock( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray <LocalFunctionSymbol> .Empty, ImmutableArray.Create <BoundStatement>( new BoundTryStatement( syntax, block, ImmutableArray <BoundCatchBlock> .Empty, new BoundBlock( syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray <LocalFunctionSymbol> .Empty, ImmutableArray.Create <BoundStatement>( baseFinalizeCall) ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true }))); } return(block); }
/// <summary> /// Add sequence point |here|: /// /// foreach (|Type var| in expr) { } /// </summary> /// <remarks> /// Hit every iteration. /// </remarks> private void AddForEachIterationVariableSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement iterationVarDecl) { if (this.GenerateDebugInfo) { TextSpan iterationVarDeclSpan = TextSpan.FromBounds(forEachSyntax.Type.SpanStart, forEachSyntax.Identifier.Span.End); iterationVarDecl = new BoundSequencePointWithSpan(forEachSyntax, iterationVarDecl, iterationVarDeclSpan); } }
/// <summary> /// Add sequence point |here|: /// /// |foreach| (Type var in expr) { } /// </summary> /// <remarks> /// Hit once, before looping begins. /// </remarks> private void AddForEachKeywordSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement result) { if (this.GenerateDebugInfo) { BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span); result = new BoundStatementList(forEachSyntax, ImmutableArray.Create <BoundStatement>(foreachKeywordSequencePoint, result)); } }
/// <summary> /// Add sequence point |here|: /// /// |foreach| (Type var in expr) { } /// </summary> /// <remarks> /// Hit once, before looping begins. /// </remarks> public override BoundStatement InstrumentForEachStatement(BoundForEachStatement original, BoundStatement rewritten) { var forEachSyntax = (CommonForEachStatementSyntax)original.Syntax; BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span); return(new BoundStatementList(forEachSyntax, ImmutableArray.Create <BoundStatement>(foreachKeywordSequencePoint, base.InstrumentForEachStatement(original, rewritten)))); }
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)); }
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, rewrittenCondition, 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))); }
/// <summary> /// Add sequence point |here|: /// /// |foreach| (Type var in expr) { } /// </summary> /// <remarks> /// Hit once, before looping begins. /// </remarks> public override BoundStatement InstrumentForEachStatement(BoundForEachStatement original, BoundStatement rewritten) { var forEachSyntax = (CommonForEachStatementSyntax)original.Syntax; var span = forEachSyntax.AwaitKeyword != default ? TextSpan.FromBounds(forEachSyntax.AwaitKeyword.Span.Start, forEachSyntax.ForEachKeyword.Span.End) : forEachSyntax.ForEachKeyword.Span; var foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, span); return(new BoundStatementList(forEachSyntax, ImmutableArray.Create <BoundStatement>(foreachKeywordSequencePoint, base.InstrumentForEachStatement(original, rewritten)))); }
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 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)); }
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 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)); }
// insert the implicit "return" statement at the end of the method body // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these // ones are going to have sequence points. internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method, CSharpSyntaxNode syntax = null) { Debug.Assert(method != null); if (syntax == null) { syntax = node.Syntax; } BoundStatement ret = method.IsIterator ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax) : BoundReturnStatement.Synthesized(syntax, null); if (syntax.Kind() == SyntaxKind.Block) { // Implicitly added return for async method does not need sequence points since lowering would add one. if (!method.IsAsync) { var blockSyntax = (BlockSyntax)syntax; ret = new BoundSequencePointWithSpan( blockSyntax, ret, blockSyntax.CloseBraceToken.Span) { WasCompilerGenerated = true }; } } switch (node.Kind) { case BoundKind.Block: var block = (BoundBlock)node; return(block.Update(block.Locals, block.Statements.Add(ret))); default: return(new BoundBlock(syntax, ImmutableArray <LocalSymbol> .Empty, ImmutableArray.Create(ret, node))); } }
// insert the implicit "return" statement at the end of the method body // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these // ones are going to have sequence points. internal static BoundBlock AppendImplicitReturn(BoundStatement node, MethodSymbol method, CSharpSyntaxNode syntax = null) { Debug.Assert(method != null); if (syntax == null) { syntax = node.Syntax; } BoundStatement ret = method.IsIterator ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax) : BoundReturnStatement.Synthesized(syntax, null); if (syntax.Kind() == SyntaxKind.Block) { // Implicitly added return for async method does not need sequence points since lowering would add one. if (!method.IsAsync) { var blockSyntax = (BlockSyntax)syntax; ret = new BoundSequencePointWithSpan( blockSyntax, ret, blockSyntax.CloseBraceToken.Span) { WasCompilerGenerated = true }; } } switch (node.Kind) { case BoundKind.Block: var block = (BoundBlock)node; return block.Update(block.Locals, block.Statements.Add(ret)); default: return new BoundBlock(syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create(ret, node)); } }
public override BoundNode VisitIfStatement(BoundIfStatement node) { Debug.Assert(node != null); var rewrittenCondition = VisitExpression(node.Condition); var rewrittenConsequence = VisitStatement(node.Consequence); var rewrittenAlternative = VisitStatement(node.AlternativeOpt); var syntax = (IfStatementSyntax)node.Syntax; var result = RewriteIfStatement(syntax, node.Locals, rewrittenCondition, rewrittenConsequence, rewrittenAlternative, node.HasErrors); // add sequence point before the whole statement if (this.generateDebugInfo && !node.WasCompilerGenerated) { result = new BoundSequencePointWithSpan( syntax, result, TextSpan.FromBounds( syntax.IfKeyword.SpanStart, syntax.CloseParenToken.Span.End), node.HasErrors); } return result; }
public override BoundNode VisitIfStatement(BoundIfStatement node) { Debug.Assert(node != null); var rewrittenCondition = VisitExpression(node.Condition); var rewrittenConsequence = VisitStatement(node.Consequence); var rewrittenAlternative = VisitStatement(node.AlternativeOpt); var syntax = (IfStatementSyntax)node.Syntax; var result = RewriteIfStatement(syntax, rewrittenCondition, rewrittenConsequence, rewrittenAlternative, node.HasErrors); // add sequence point before the whole statement if (this.generateDebugInfo && !node.WasCompilerGenerated) { result = new BoundSequencePointWithSpan( syntax, result, TextSpan.FromBounds( syntax.IfKeyword.SpanStart, syntax.CloseParenToken.Span.End), node.HasErrors); } return(result); }
internal static BoundBlock ConstructDestructorBody(CSharpSyntaxNode syntax, MethodSymbol method, BoundBlock block) { Debug.Assert(method.MethodKind == MethodKind.Destructor); Debug.Assert(syntax.Kind == SyntaxKind.Block); // If this is a destructor and a base type has a Finalize method (see GetBaseTypeFinalizeMethod for exact // requirements), then we need to call that method in a finally block. Otherwise, just return block as-is. // NOTE: the Finalize method need not be a destructor or be overridden by the current method. MethodSymbol baseTypeFinalize = GetBaseTypeFinalizeMethod(method); if ((object)baseTypeFinalize != null) { BoundStatement baseFinalizeCall = new BoundSequencePointWithSpan( //sequence point to mimic Dev10 syntax, new BoundExpressionStatement( syntax, BoundCall.Synthesized( syntax, new BoundBaseReference( syntax, method.ContainingType) { WasCompilerGenerated = true }, baseTypeFinalize) ) { WasCompilerGenerated = true }, ((BlockSyntax)syntax).CloseBraceToken.Span); return new BoundBlock( syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create<BoundStatement>( new BoundTryStatement( syntax, block, ImmutableArray<BoundCatchBlock>.Empty, new BoundBlock( syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray.Create<BoundStatement>( baseFinalizeCall) ) { WasCompilerGenerated = true } ) { WasCompilerGenerated = true })); } return block; }
/// <summary> /// Lower "using (expression) statement" to a try-finally block. /// </summary> private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { return(tryBlock); } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; CSharpSyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; BoundExpression tempInit = MakeConversion( expressionSyntax, rewrittenExpression, node.IDisposableConversion.Kind, this.compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); } else { // ResourceType temp = expr; boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.generateDebugInfo) { // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression. expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return(new BoundBlock( syntax: usingSyntax, localsOpt: ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol), statements: ImmutableArray.Create <BoundStatement>(expressionStatement, tryFinally))); }
/// <summary> /// Add sequence point |here|: /// /// |foreach| (Type var in expr) { } /// </summary> /// <remarks> /// Hit once, before looping begins. /// </remarks> public override BoundStatement InstrumentForEachStatement(BoundForEachStatement original, BoundStatement rewritten) { var forEachSyntax = (CommonForEachStatementSyntax)original.Syntax; BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span); return new BoundStatementList(forEachSyntax, ImmutableArray.Create<BoundStatement>(foreachKeywordSequencePoint, base.InstrumentForEachStatement(original, rewritten))); }
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, 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); }
/// <summary> /// Add sequence point |here|: /// /// |foreach| (Type var in expr) { } /// </summary> /// <remarks> /// Hit once, before looping begins. /// </remarks> private void AddForEachKeywordSequencePoint(ForEachStatementSyntax forEachSyntax, ref BoundStatement result) { if (this.GenerateDebugInfo) { BoundSequencePointWithSpan foreachKeywordSequencePoint = new BoundSequencePointWithSpan(forEachSyntax, null, forEachSyntax.ForEachKeyword.Span); result = new BoundStatementList(forEachSyntax, ImmutableArray.Create<BoundStatement>(foreachKeywordSequencePoint, result)); } }
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); }
/// <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 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)); }
/// <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))))); } }
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)); }
// insert the implicit "return" statement at the end of the method body // Normally, we wouldn't bother attaching syntax trees to compiler-generated nodes, but these // ones are going to have sequence points. internal static BoundBlock AppendImplicitReturn(BoundBlock body, MethodSymbol method, CSharpSyntaxNode syntax = null) { Debug.Assert(body != null); Debug.Assert(method != null); if (syntax == null) { syntax = body.Syntax; } Debug.Assert(body.WasCompilerGenerated || syntax.IsKind(SyntaxKind.Block) || syntax.IsKind(SyntaxKind.ArrowExpressionClause)); BoundStatement ret = method.IsIterator ? (BoundStatement)BoundYieldBreakStatement.Synthesized(syntax) : BoundReturnStatement.Synthesized(syntax, null); // Implicitly added return for async method does not need sequence points since lowering would add one. if (syntax.IsKind(SyntaxKind.Block) && !method.IsAsync) { var blockSyntax = (BlockSyntax)syntax; ret = new BoundSequencePointWithSpan( blockSyntax, ret, blockSyntax.CloseBraceToken.Span) { WasCompilerGenerated = true }; } return body.Update(body.Locals, body.Statements.Add(ret)); }
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)); }
/// <summary> /// Lower "using (expression) statement" to a try-finally block. /// </summary> private BoundBlock RewriteExpressionUsingStatement(BoundUsingStatement node, BoundBlock tryBlock) { Debug.Assert(node.ExpressionOpt != null); Debug.Assert(node.DeclarationsOpt == null); // See comments in BuildUsingTryFinally for the details of the lowering to try-finally. // // SPEC: A using statement of the form "using (expression) statement; " has the // SPEC: same three possible expansions [ as "using (ResourceType r = expression) statement; ] // SPEC: but in this case ResourceType is implicitly the compile-time type of the expression, // SPEC: and the resource variable is inaccessible to and invisible to the embedded statement. // // DELIBERATE SPEC VIOLATION: // // The spec quote above implies that the expression must have a type; in fact we allow // the expression to be null. // // If expr is the constant null then we can elide the whole thing and simply generate the statement. BoundExpression rewrittenExpression = (BoundExpression)Visit(node.ExpressionOpt); if (rewrittenExpression.ConstantValue == ConstantValue.Null) { return tryBlock; } // Otherwise, we lower "using(expression) statement;" as follows: // // * If the expression is of type dynamic then we lower as though the user had written // // using(IDisposable temp = (IDisposable)expression) statement; // // Note that we have to do the conversion early, not in the finally block, because // if the conversion fails at runtime with an exception then the exception must happen // before the statement runs. // // * Otherwise we lower as though the user had written // // using(ResourceType temp = expression) statement; // TypeSymbol expressionType = rewrittenExpression.Type; CSharpSyntaxNode expressionSyntax = rewrittenExpression.Syntax; UsingStatementSyntax usingSyntax = (UsingStatementSyntax)node.Syntax; BoundAssignmentOperator tempAssignment; BoundLocal boundTemp; if ((object)expressionType == null || expressionType.IsDynamic()) { // IDisposable temp = (IDisposable) expr; BoundExpression tempInit = MakeConversion( expressionSyntax, rewrittenExpression, node.IDisposableConversion.Kind, this.compilation.GetSpecialType(SpecialType.System_IDisposable), @checked: false, constantValueOpt: rewrittenExpression.ConstantValue); boundTemp = this.factory.StoreToTemp(tempInit, out tempAssignment); } else { // ResourceType temp = expr; boundTemp = this.factory.StoreToTemp(rewrittenExpression, tempKind: TempKind.Using, store: out tempAssignment); } BoundStatement expressionStatement = new BoundExpressionStatement(expressionSyntax, tempAssignment); if (this.generateDebugInfo) { // NOTE: unlike in the assignment case, the sequence point is on the using keyword, not the expression. expressionStatement = new BoundSequencePointWithSpan(usingSyntax, expressionStatement, usingSyntax.UsingKeyword.Span); } BoundStatement tryFinally = RewriteUsingStatementTryFinally(usingSyntax, tryBlock, boundTemp); // { ResourceType temp = expr; try { ... } finally { ... } } return new BoundBlock( syntax: usingSyntax, localsOpt: ImmutableArray.Create<LocalSymbol>(boundTemp.LocalSymbol), statements: ImmutableArray.Create<BoundStatement>(expressionStatement, tryFinally)); }