Skip to content

Commit a2bd583

Browse files
TylerBrinkleyeiriktsarpalistannergooding
authoredApr 24, 2024
Make mutable generic collection interfaces implement read-only collection interfaces (dotnet#95830)
* Make mutable generic collection interfaces implement read-only collection interfaces * More updates * Fix build * Update refs * Add DIM's to ref also * fixes * Try to fix arrays * Moved dim's to the end of the interfaces. Made the tests support .NET Framework 4.8. * Small fix * Small fix * Fix build * Fix * Cleanup * Incorporate dotnet#96672 * Check the correct inheritanceDepth in vm/array.cpp --------- Co-authored-by: Eirik Tsarpalis <eirik.tsarpalis@gmail.com> Co-authored-by: Tanner Gooding <tagoo@outlook.com>
1 parent f55c5a8 commit a2bd583

File tree

13 files changed

+490
-164
lines changed

13 files changed

+490
-164
lines changed
 

‎src/coreclr/System.Private.CoreLib/src/System/Array.CoreCLR.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -694,7 +694,7 @@ public ArrayInitializeCache(RuntimeType arrayType)
694694
// it for type <T> and executes it.
695695
//
696696
// The "T" will reflect the interface used to invoke the method. The actual runtime "this" will be
697-
// array that is castable to "T[]" (i.e. for primitivs and valuetypes, it will be exactly
697+
// array that is castable to "T[]" (i.e. for primitives and valuetypes, it will be exactly
698698
// "T[]" - for orefs, it may be a "U[]" where U derives from T.)
699699
//----------------------------------------------------------------------------------------
700700
internal sealed class SZArrayHelper

‎src/coreclr/vm/array.cpp

+2-17
Original file line numberDiff line numberDiff line change
@@ -1210,29 +1210,14 @@ MethodDesc* GetActualImplementationForArrayGenericIListOrIReadOnlyListMethod(Met
12101210
}
12111211
CONTRACTL_END
12121212

1213-
int slot = pItfcMeth->GetSlot();
1214-
1215-
// We need to pick the right starting method depending on the depth of the inheritance chain
1216-
static const BinderMethodID startingMethod[] = {
1217-
METHOD__SZARRAYHELPER__GETENUMERATOR, // First method of IEnumerable`1
1218-
METHOD__SZARRAYHELPER__GET_COUNT, // First method of ICollection`1/IReadOnlyCollection`1
1219-
METHOD__SZARRAYHELPER__GET_ITEM // First method of IList`1/IReadOnlyList`1
1220-
};
1221-
12221213
// Subtract one for the non-generic IEnumerable that the generic enumerable inherits from
12231214
unsigned int inheritanceDepth = pItfcMeth->GetMethodTable()->GetNumInterfaces() - 1;
1224-
PREFIX_ASSUME(0 <= inheritanceDepth && inheritanceDepth < ARRAY_SIZE(startingMethod));
1225-
1226-
MethodDesc *pGenericImplementor = CoreLibBinder::GetMethod((BinderMethodID)(startingMethod[inheritanceDepth] + slot));
12271215

1228-
// The most common reason for this assert is that the order of the SZArrayHelper methods in
1229-
// corelib.h does not match the order they are implemented on the generic interfaces.
1230-
_ASSERTE(pGenericImplementor == MemberLoader::FindMethodByName(g_pSZArrayHelperClass, pItfcMeth->GetName()));
1216+
MethodDesc *pGenericImplementor = MemberLoader::FindMethodByName(g_pSZArrayHelperClass, pItfcMeth->GetName());
12311217

12321218
// OPTIMIZATION: For any method other than GetEnumerator(), we can safely substitute
12331219
// "Object" for reference-type theT's. This causes fewer methods to be instantiated.
1234-
if (startingMethod[inheritanceDepth] != METHOD__SZARRAYHELPER__GETENUMERATOR &&
1235-
!theT.IsValueType())
1220+
if (inheritanceDepth != 0 && !theT.IsValueType())
12361221
{
12371222
theT = TypeHandle(g_pObjectClass);
12381223
}

‎src/libraries/Common/tests/System/Collections/CollectionAsserts.cs

+152
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System;
45
using System.Collections.Generic;
56
using System.Linq;
67
using Xunit;
@@ -9,6 +10,151 @@ namespace System.Collections.Tests
910
{
1011
internal static class CollectionAsserts
1112
{
13+
public static void HasCount<T>(ICollection<T> collection, int count)
14+
{
15+
Assert.Equal(count, collection.Count);
16+
#if !NETFRAMEWORK
17+
IReadOnlyCollection<T> readOnlyCollection = collection;
18+
Assert.Equal(count, readOnlyCollection.Count);
19+
#endif
20+
}
21+
22+
public static void EqualAt<T>(IList<T> list, int index, T expected)
23+
{
24+
Assert.Equal(expected, list[index]);
25+
#if !NETFRAMEWORK
26+
IReadOnlyList<T> readOnlyList = list;
27+
Assert.Equal(expected, readOnlyList[index]);
28+
#endif
29+
}
30+
31+
public static void NotEqualAt<T>(IList<T> list, int index, T expected)
32+
{
33+
Assert.NotEqual(expected, list[index]);
34+
#if !NETFRAMEWORK
35+
IReadOnlyList<T> readOnlyList = list;
36+
Assert.NotEqual(expected, readOnlyList[index]);
37+
#endif
38+
}
39+
40+
public static void ThrowsElementAt<T>(IList<T> list, int index, Type exceptionType)
41+
{
42+
Assert.Throws(exceptionType, () => list[index]);
43+
#if !NETFRAMEWORK
44+
IReadOnlyList<T> readOnlyList = list;
45+
Assert.Throws(exceptionType, () => readOnlyList[index]);
46+
#endif
47+
}
48+
49+
public static void ElementAtSucceeds<T>(IList<T> list, int index)
50+
{
51+
T result = list[index];
52+
#if !NETFRAMEWORK
53+
IReadOnlyList<T> readOnlyList = list;
54+
Assert.Equal(result, readOnlyList[index]);
55+
#endif
56+
}
57+
58+
public static void EqualAt<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key, TValue expected)
59+
{
60+
Assert.Equal(expected, dictionary[key]);
61+
#if !NETFRAMEWORK
62+
IReadOnlyDictionary<TKey, TValue> readOnlyDictionary = dictionary;
63+
Assert.Equal(expected, readOnlyDictionary[key]);
64+
#endif
65+
}
66+
67+
public static void ContainsKey<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key, bool expected)
68+
{
69+
Assert.Equal(expected, dictionary.ContainsKey(key));
70+
#if !NETFRAMEWORK
71+
IReadOnlyDictionary<TKey, TValue> readOnlyDictionary = dictionary;
72+
Assert.Equal(expected, readOnlyDictionary.ContainsKey(key));
73+
#endif
74+
}
75+
76+
public static void TryGetValue<TKey, TValue>(IDictionary<TKey, TValue> dictionary, TKey key, bool expected, TValue expectedValue = default)
77+
{
78+
Assert.Equal(expected, dictionary.TryGetValue(key, out TValue value));
79+
if (expected)
80+
{
81+
Assert.Equal(expectedValue, value);
82+
}
83+
#if !NETFRAMEWORK
84+
IReadOnlyDictionary<TKey, TValue> readOnlyDictionary = dictionary;
85+
Assert.Equal(expected, readOnlyDictionary.TryGetValue(key, out value));
86+
if (expected)
87+
{
88+
Assert.Equal(expectedValue, value);
89+
}
90+
#endif
91+
}
92+
93+
public static void Contains<T>(ISet<T> set, T expected)
94+
{
95+
Assert.True(set.Contains(expected));
96+
#if !NETFRAMEWORK
97+
ICollection<T> collection = set;
98+
Assert.True(collection.Contains(expected));
99+
IReadOnlySet<T> readOnlySet = set;
100+
Assert.True(readOnlySet.Contains(expected));
101+
#endif
102+
}
103+
104+
public static void IsProperSubsetOf<T>(ISet<T> set, IEnumerable<T> enumerable, bool expected)
105+
{
106+
Assert.Equal(expected, set.IsProperSubsetOf(enumerable));
107+
#if !NETFRAMEWORK
108+
IReadOnlySet<T> readOnlySet = set;
109+
Assert.Equal(expected, readOnlySet.IsProperSubsetOf(enumerable));
110+
#endif
111+
}
112+
113+
public static void IsProperSupersetOf<T>(ISet<T> set, IEnumerable<T> enumerable, bool expected)
114+
{
115+
Assert.Equal(expected, set.IsProperSupersetOf(enumerable));
116+
#if !NETFRAMEWORK
117+
IReadOnlySet<T> readOnlySet = set;
118+
Assert.Equal(expected, readOnlySet.IsProperSupersetOf(enumerable));
119+
#endif
120+
}
121+
122+
public static void IsSubsetOf<T>(ISet<T> set, IEnumerable<T> enumerable, bool expected)
123+
{
124+
Assert.Equal(expected, set.IsSubsetOf(enumerable));
125+
#if !NETFRAMEWORK
126+
IReadOnlySet<T> readOnlySet = set;
127+
Assert.Equal(expected, readOnlySet.IsSubsetOf(enumerable));
128+
#endif
129+
}
130+
131+
public static void IsSupersetOf<T>(ISet<T> set, IEnumerable<T> enumerable, bool expected)
132+
{
133+
Assert.Equal(expected, set.IsSupersetOf(enumerable));
134+
#if !NETFRAMEWORK
135+
IReadOnlySet<T> readOnlySet = set;
136+
Assert.Equal(expected, readOnlySet.IsSupersetOf(enumerable));
137+
#endif
138+
}
139+
140+
public static void Overlaps<T>(ISet<T> set, IEnumerable<T> enumerable, bool expected)
141+
{
142+
Assert.Equal(expected, set.Overlaps(enumerable));
143+
#if !NETFRAMEWORK
144+
IReadOnlySet<T> readOnlySet = set;
145+
Assert.Equal(expected, readOnlySet.Overlaps(enumerable));
146+
#endif
147+
}
148+
149+
public static void SetEquals<T>(ISet<T> set, IEnumerable<T> enumerable, bool expected)
150+
{
151+
Assert.Equal(expected, set.SetEquals(enumerable));
152+
#if !NETFRAMEWORK
153+
IReadOnlySet<T> readOnlySet = set;
154+
Assert.Equal(expected, readOnlySet.SetEquals(enumerable));
155+
#endif
156+
}
157+
12158
public static void Equal(ICollection expected, ICollection actual)
13159
{
14160
Assert.Equal(expected == null, actual == null);
@@ -43,6 +189,12 @@ public static void Equal<T>(ICollection<T> expected, ICollection<T> actual)
43189
return;
44190
}
45191
Assert.Equal(expected.Count, actual.Count);
192+
#if !NETFRAMEWORK
193+
IReadOnlyCollection<T> readOnlyExpected = expected;
194+
Assert.Equal(expected.Count, readOnlyExpected.Count);
195+
IReadOnlyCollection<T> readOnlyActual = actual;
196+
Assert.Equal(actual.Count, readOnlyActual.Count);
197+
#endif
46198
IEnumerator<T> e = expected.GetEnumerator();
47199
IEnumerator<T> a = actual.GetEnumerator();
48200
while (e.MoveNext())

0 commit comments

Comments
 (0)
Please sign in to comment.