File: CodeGen\StateMachineStateDebugInfo.cs
Web Access
Project: src\src\Compilers\Core\Portable\Microsoft.CodeAnalysis.csproj (Microsoft.CodeAnalysis)
// 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;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.Emit;
 
namespace Microsoft.CodeAnalysis.CodeGen;
 
internal readonly struct StateMachineStateDebugInfo(int syntaxOffset, AwaitDebugId awaitId, StateMachineState stateNumber)
{
    public readonly int SyntaxOffset = syntaxOffset;
    public readonly AwaitDebugId AwaitId = awaitId;
    public readonly StateMachineState StateNumber = stateNumber;
}
 
/// <summary>
/// Debug information maintained for each state machine.
/// Facilitates mapping of state machine states from a compilation to the previous one (or to a metadata baseline) during EnC.
/// </summary>
internal readonly struct StateMachineStatesDebugInfo
{
    public readonly ImmutableArray<StateMachineStateDebugInfo> States;
 
    /// <summary>
    /// The number of the first state that has not been used in any of the previous versions of the state machine,
    /// or null if we are not generating EnC delta.
    /// 
    /// For 1st generation EnC delta, this is calculated by examining the <see cref="EditAndContinueMethodDebugInformation.StateMachineStates"/> stored in the baseline metadata.
    /// For subsequent generations, the number is updated to account for newly generated states in that generation.
    /// </summary>
    public readonly StateMachineState? FirstUnusedIncreasingStateMachineState;
 
    /// <summary>
    /// The number of the first state that has not been used in any of the previous versions of the state machine,
    /// or null if we are not generating EnC delta, or the state machine has no decreasing states.
    /// 
    /// For 1st generation EnC delta, this is calculated by examining the <see cref="EditAndContinueMethodDebugInformation.StateMachineStates"/> stored in the baseline metadata.
    /// For subsequent generations, the number is updated to account for newly generated states in that generation.
    /// </summary>
    public readonly StateMachineState? FirstUnusedDecreasingStateMachineState;
 
    private StateMachineStatesDebugInfo(ImmutableArray<StateMachineStateDebugInfo> states, StateMachineState? firstUnusedIncreasingStateMachineState, StateMachineState? firstUnusedDecreasingStateMachineState)
    {
        States = states;
        FirstUnusedIncreasingStateMachineState = firstUnusedIncreasingStateMachineState;
        FirstUnusedDecreasingStateMachineState = firstUnusedDecreasingStateMachineState;
    }
 
    public static StateMachineStatesDebugInfo Create(VariableSlotAllocator? variableSlotAllocator, ImmutableArray<StateMachineStateDebugInfo> stateInfos)
    {
        StateMachineState? firstUnusedIncreasingStateMachineState = null, firstUnusedDecreasingStateMachineState = null;
 
        if (variableSlotAllocator != null)
        {
            // We start with first unused state numbers from the previous generation and update them based on states generated in the current one.
            firstUnusedIncreasingStateMachineState = variableSlotAllocator.GetFirstUnusedStateMachineState(increasing: true);
            firstUnusedDecreasingStateMachineState = variableSlotAllocator.GetFirstUnusedStateMachineState(increasing: false);
 
            if (!stateInfos.IsDefaultOrEmpty)
            {
                // The current method is a state machine and has some resumable/finalization states.
                // Update the first unused states based on the highest resumable (positive numbers) and lowest finalization (negative numbers) of this method.
 
                var maxState = stateInfos.Max(info => info.StateNumber) + 1;
                var minState = stateInfos.Min(info => info.StateNumber) - 1;
 
                firstUnusedIncreasingStateMachineState = (firstUnusedIncreasingStateMachineState != null) ? (StateMachineState)Math.Max((int)firstUnusedIncreasingStateMachineState.Value, (int)maxState) : maxState;
 
                if (minState < 0)
                {
                    firstUnusedDecreasingStateMachineState = (firstUnusedDecreasingStateMachineState != null) ? (StateMachineState)Math.Min((int)firstUnusedDecreasingStateMachineState.Value, (int)minState) : minState;
                }
            }
        }
 
        return new StateMachineStatesDebugInfo(
            stateInfos,
            firstUnusedIncreasingStateMachineState,
            firstUnusedDecreasingStateMachineState);
    }
}