File: Contracts\EditAndContinue\ManagedModuleMethodId.cs
Web Access
Project: src\src\Features\Core\Portable\Microsoft.CodeAnalysis.Features.csproj (Microsoft.CodeAnalysis.Features)
// 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.Runtime.Serialization;
using System;
using System.Diagnostics;
 
namespace Microsoft.CodeAnalysis.Contracts.EditAndContinue;
 
/// <summary>
/// ManagedModuleMethodId is a token/version pair which is used to uniquely identify the
/// symbol store's understanding of a particular CLR method within a module context.
/// See <see cref="ManagedMethodId"/> for more details.
/// </summary>
[DataContract]
[DebuggerDisplay("{GetDebuggerDisplay(),nq}")]
internal readonly struct ManagedModuleMethodId : IEquatable<ManagedModuleMethodId>
{
    /// <summary>
    /// Creates a ManagedModuleMethodId.
    /// </summary>
    /// <param name="token">Method token.</param>
    /// <param name="version">Method version.</param>
    /// <exception cref="ArgumentOutOfRangeException">
    /// If <paramref name="token"/> is less or equals 0x06000000 or <paramref name="version"/> is less or equals zero. 
    /// </exception>
    public ManagedModuleMethodId(
        int token,
        int version)
    {
        // 0x06 means that the token is for a MethodDef.
        // Valid method tokens are expected to be greather than 0x06000000.
        if (token <= 0x06000000)
            throw new ArgumentOutOfRangeException(nameof(token));
        if (version <= 0)
            throw new ArgumentOutOfRangeException(nameof(version));
 
        Token = token;
        Version = version;
    }
 
    /// <summary>
    /// The method definition metadata token of the method that contains this symbol.
    /// </summary>
    [DataMember(Name = "token")]
    public int Token { get; }
 
    /// <summary>
    /// MethodVersion is a 1-based index. This will be '1' for methods that have not
    /// been edited through Edit-and-continue. For edited methods, the version indicates
    /// the EnC apply of this method.
    /// Thus, if the user does 5 EnC applies and a particular method is only edited in the 5th apply, 
    /// then there are two method ids for this method, and they have Version = 1 and Version = 5.
    ///
    /// The debugger needs to deal with old versions of the method because they will
    /// continue to be on the call stack until control is unwound.The debugger can also hit
    /// breakpoints or stop for exceptions within exception handling regions of old
    /// methods. In other words, if the user sets a breakpoint within the catch block of a
    /// non-leaf method, the debugger needs to set that breakpoint within the old version
    /// of the method.
    /// 
    /// In scenarios such as function breakpoint binding, the value '0' may used to
    /// indicate the current version of the method.
    /// </summary>
    [DataMember(Name = "version")]
    public int Version { get; }
 
    public bool Equals(ManagedModuleMethodId other)
    {
        return Token == other.Token && Version == other.Version;
    }
 
    public override bool Equals(object? obj) => obj is ManagedModuleMethodId method && Equals(method);
 
    public override int GetHashCode() => Token ^ Version;
 
    public static bool operator ==(ManagedModuleMethodId left, ManagedModuleMethodId right) => left.Equals(right);
 
    public static bool operator !=(ManagedModuleMethodId left, ManagedModuleMethodId right) => !(left == right);
 
    internal string GetDebuggerDisplay() => $"0x{Token:X8} v{Version}";
}