diff --git a/src/Compilers/Core/Portable/CodeGen/TokenMap.cs b/src/Compilers/Core/Portable/CodeGen/TokenMap.cs index 1f9f102941800..fecdfeffa91da 100644 --- a/src/Compilers/Core/Portable/CodeGen/TokenMap.cs +++ b/src/Compilers/Core/Portable/CodeGen/TokenMap.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.Threading; using Microsoft.Cci; @@ -28,9 +29,7 @@ internal sealed class TokenMap private object[] _items = Array.Empty(); private int _count = 0; - internal TokenMap() - { - } + internal TokenMap() { } public uint GetOrAddTokenFor(IReference item, out bool referenceAdded) { @@ -95,6 +94,10 @@ private uint AddItem(IReferenceOrISignatureEquivalent item, out bool referenceAd public object GetItem(uint token) { + // If a token has been handed out, then it should be always within _count of the + // current array and a lock is not required. + Debug.Assert(token < (uint)_count && _count <= _items.Length); + return _items[(int)token]; } @@ -102,11 +105,15 @@ public object GetItem(uint token) // should probably return ROA instead of IE and cache that in Module. (and no need to return count) public ReadOnlySpan GetAllItems() { + // Read _count before _items reference, to match inverse of the writes in AddItem. + // So _items is guaranteed to have at least count items; and a lock is not required. + // Read the count prior to getting the array int count = Volatile.Read(ref _count); + // Read the array reference object[] items = Volatile.Read(ref _items); - // Return a right sized copy of the array + // Return a right sized view of the array based on read count and reference. return new ReadOnlySpan(items, 0, count); } } diff --git a/src/Compilers/Core/Portable/IReferenceOrISignature.cs b/src/Compilers/Core/Portable/IReferenceOrISignature.cs index da1572985fcad..a83a17ec968b4 100644 --- a/src/Compilers/Core/Portable/IReferenceOrISignature.cs +++ b/src/Compilers/Core/Portable/IReferenceOrISignature.cs @@ -21,35 +21,22 @@ namespace Microsoft.CodeAnalysis /// internal readonly struct IReferenceOrISignatureEquivalent : IEquatable { - private readonly object? _item; + private readonly object _item; - public IReferenceOrISignatureEquivalent(IReference item) - { - _item = item; - } + public IReferenceOrISignatureEquivalent(IReference item) => _item = item; - public IReferenceOrISignatureEquivalent(ISignature item) - { - _item = item; - } + public IReferenceOrISignatureEquivalent(ISignature item) => _item = item; // Needed to resolve ambiguity for types that implement both IReference and ISignature - public IReferenceOrISignatureEquivalent(IMethodReference item) - { - _item = item; - } + public IReferenceOrISignatureEquivalent(IMethodReference item) => _item = item; [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Equals(IReferenceOrISignatureEquivalent other) { // Fast inlinable ReferenceEquals - object? x = _item; - object? y = other._item; - if (x is null) - { - return y is null; - } - else if (ReferenceEquals(x, y)) + var x = _item; + var y = other._item; + if (ReferenceEquals(x, y)) { return true; } @@ -57,7 +44,7 @@ public bool Equals(IReferenceOrISignatureEquivalent other) return EqualsSlow(x, y); } - private static bool EqualsSlow(object x, object? y) + private static bool EqualsSlow(object x, object y) { if (x is ISymbolInternal sx && y is ISymbolInternal sy) { @@ -75,11 +62,11 @@ private static bool EqualsSlow(object x, object? y) public override bool Equals(object? obj) => false; - public override int GetHashCode() => _item?.GetHashCode() ?? 0; + public override int GetHashCode() => _item.GetHashCode(); - public override string ToString() => _item?.ToString() ?? "null"; + public override string ToString() => _item.ToString() ?? "null"; - internal object AsObject() => _item!; + internal object AsObject() => _item; } /// @@ -87,23 +74,14 @@ private static bool EqualsSlow(object x, object? y) /// internal readonly struct IReferenceOrISignature : IEquatable { - private readonly object? _item; + private readonly object _item; - public IReferenceOrISignature(IReference item) - { - _item = item; - } + public IReferenceOrISignature(IReference item) => _item = item; - public IReferenceOrISignature(ISignature item) - { - _item = item; - } + public IReferenceOrISignature(ISignature item) => _item = item; // Used by implicit conversion - private IReferenceOrISignature(object? item) - { - _item = item; - } + private IReferenceOrISignature(object item) => _item = item; public static implicit operator IReferenceOrISignature(IReferenceOrISignatureEquivalent item) => new IReferenceOrISignature(item.AsObject()); @@ -112,8 +90,8 @@ public static implicit operator IReferenceOrISignature(IReferenceOrISignatureEqu public override bool Equals(object? obj) => false; - public override int GetHashCode() => _item?.GetHashCode() ?? 0; + public override int GetHashCode() => _item.GetHashCode(); - public override string ToString() => _item?.ToString() ?? "null"; + public override string ToString() => _item.ToString() ?? "null"; } }