File: Lowering\LocalRewriter\LocalRewriter_Block.cs
Web Access
Project: src\src\Compilers\CSharp\Portable\Microsoft.CodeAnalysis.CSharp.csproj (Microsoft.CodeAnalysis.CSharp)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Collections;
 
namespace Microsoft.CodeAnalysis.CSharp
{
    internal sealed partial class LocalRewriter
    {
        public override BoundNode VisitBlock(BoundBlock node)
        {
            if (Instrument)
            {
                Instrumenter.PreInstrumentBlock(node, this);
            }
 
            var builder = ArrayBuilder<BoundStatement>.GetInstance();
            // If _additionalLocals is null, this must be the outermost block of the current function.
            // If so, create a collection where child statements can insert inline array temporaries,
            // and add those temporaries to the generated block.
            var previousLocals = _additionalLocals;
            if (previousLocals is null)
            {
                _additionalLocals = ArrayBuilder<LocalSymbol>.GetInstance();
            }
 
            try
            {
                VisitStatementSubList(builder, node.Statements);
 
                var additionalLocals = TemporaryArray<LocalSymbol>.Empty;
 
                BoundBlockInstrumentation? instrumentation = null;
                if (Instrument)
                {
                    Instrumenter.InstrumentBlock(node, this, ref additionalLocals, out var prologue, out var epilogue, out instrumentation);
                    if (prologue != null)
                    {
                        builder.Insert(0, prologue);
                    }
 
                    if (epilogue != null)
                    {
                        builder.Add(epilogue);
                    }
                }
 
                var locals = node.Locals;
                if (previousLocals is null)
                {
                    locals = locals.AddRange(_additionalLocals!);
                }
                locals = locals.AddRange(additionalLocals);
                return new BoundBlock(node.Syntax, locals, node.LocalFunctions, node.HasUnsafeModifier, instrumentation, builder.ToImmutableAndFree(), node.HasErrors);
            }
            finally
            {
                if (previousLocals is null)
                {
                    _additionalLocals!.Free();
                    _additionalLocals = previousLocals;
                }
            }
        }
 
        /// <summary>
        /// Visit a partial list of statements that possibly contain using declarations
        /// </summary>
        /// <param name="builder">The array builder to append statements to</param>
        /// <param name="statements">The list of statements to visit</param>
        /// <param name="startIndex">The index of the <paramref name="statements"/> to begin visiting at</param>
        /// <returns>An <see cref="ImmutableArray{T}"/> of <see cref="BoundStatement"/></returns>
        public void VisitStatementSubList(ArrayBuilder<BoundStatement> builder, ImmutableArray<BoundStatement> statements, int startIndex = 0)
        {
            for (int i = startIndex; i < statements.Length; i++)
            {
                BoundStatement? statement = VisitPossibleUsingDeclaration(statements[i], statements, i, out var replacedUsingDeclarations);
                if (statement != null)
                {
                    builder.Add(statement);
                }
 
                if (replacedUsingDeclarations)
                {
                    break;
                }
            }
        }
 
        /// <summary>
        /// Visits a node that is possibly a <see cref="BoundUsingLocalDeclarations"/>
        /// </summary>
        /// <param name="node">The node to visit</param>
        /// <param name="statements">All statements in the block containing this node</param>
        /// <param name="statementIndex">The current statement being visited in <paramref name="statements"/></param>
        /// <param name="replacedLocalDeclarations">Set to true if this visited a <see cref="BoundUsingLocalDeclarations"/> node</param>
        /// <returns>A <see cref="BoundStatement"/></returns>
        /// <remarks>
        /// The node being visited is not necessarily equal to statements[startIndex]. 
        /// When traversing down a set of labels, we set node to the label.body and recurse, but statements[startIndex] still refers to the original parent label 
        /// as we haven't actually moved down the original statement list
        /// </remarks>
        public BoundStatement? VisitPossibleUsingDeclaration(BoundStatement node, ImmutableArray<BoundStatement> statements, int statementIndex, out bool replacedLocalDeclarations)
        {
            switch (node.Kind)
            {
                case BoundKind.LabeledStatement:
                    var labelStatement = (BoundLabeledStatement)node;
                    return MakeLabeledStatement(labelStatement, VisitPossibleUsingDeclaration(labelStatement.Body, statements, statementIndex, out replacedLocalDeclarations));
                case BoundKind.UsingLocalDeclarations:
                    // visit everything after this node 
                    ArrayBuilder<BoundStatement> builder = ArrayBuilder<BoundStatement>.GetInstance();
                    VisitStatementSubList(builder, statements, statementIndex + 1);
                    // make a using declaration with the visited statements as its body
                    replacedLocalDeclarations = true;
                    return MakeLocalUsingDeclarationStatement((BoundUsingLocalDeclarations)node, builder.ToImmutableAndFree());
                default:
                    replacedLocalDeclarations = false;
                    return VisitStatement(node);
            }
        }
 
        public override BoundNode VisitNoOpStatement(BoundNoOpStatement node)
        {
            return (node.WasCompilerGenerated || !this.Instrument)
                ? new BoundBlock(node.Syntax, ImmutableArray<LocalSymbol>.Empty, ImmutableArray<BoundStatement>.Empty)
                : Instrumenter.InstrumentNoOpStatement(node, node);
        }
    }
}