Skip to content

Commit fe67c3f

Browse files
authored
Remove System.Text.Json's DynamicDependencies on Collections.Immutable (#53317)
This saves roughly 28 KB compressed in a default blazor wasm app. Fix #53256
1 parent edaa6d7 commit fe67c3f

20 files changed

+30
-402
lines changed

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/IEnumerableConverterFactoryHelpers.cs

Lines changed: 6 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ internal static class IEnumerableConverterFactoryHelpers
4040
private const string ImmutableSortedDictionaryTypeName = "System.Collections.Immutable.ImmutableSortedDictionary";
4141

4242
private const string CreateRangeMethodName = "CreateRange";
43-
private const string CreateRangeMethodNameForEnumerable = "CreateRange`1";
44-
private const string CreateRangeMethodNameForDictionary = "CreateRange`2";
4543

46-
private const string ImmutableCollectionsAssembly = "System.Collections.Immutable";
44+
// Don't use DynamicDependency attributes to the Immutable Collection types so they can be trimmed in applications that don't use Immutable Collections.
45+
internal const string ImmutableConvertersUnreferencedCodeMessage = "System.Collections.Immutable converters use Reflection to find and create Immutable Collection types, which requires unreferenced code.";
4746

4847
internal static Type? GetCompatibleGenericBaseClass(this Type type, Type baseType)
4948
{
@@ -151,16 +150,7 @@ public static bool IsImmutableEnumerableType(this Type type)
151150
}
152151
}
153152

154-
[DynamicDependency(CreateRangeMethodNameForEnumerable, ImmutableArrayTypeName, ImmutableCollectionsAssembly)]
155-
[DynamicDependency(CreateRangeMethodNameForEnumerable, ImmutableListTypeName, ImmutableCollectionsAssembly)]
156-
[DynamicDependency(CreateRangeMethodNameForEnumerable, ImmutableStackTypeName, ImmutableCollectionsAssembly)]
157-
[DynamicDependency(CreateRangeMethodNameForEnumerable, ImmutableQueueTypeName, ImmutableCollectionsAssembly)]
158-
[DynamicDependency(CreateRangeMethodNameForEnumerable, ImmutableSortedSetTypeName, ImmutableCollectionsAssembly)]
159-
[DynamicDependency(CreateRangeMethodNameForEnumerable, ImmutableHashSetTypeName, ImmutableCollectionsAssembly)]
160-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
161-
Justification = "The CreateRange method is preserved by the DynamicDependency attribute.")]
162-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
163-
Justification = "Immutable collections don't have trimming annotations.")]
153+
[RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)]
164154
public static MethodInfo GetImmutableEnumerableCreateRangeMethod(this Type type, Type elementType)
165155
{
166156
Type? constructingType = GetImmutableEnumerableConstructingType(type);
@@ -183,12 +173,7 @@ public static MethodInfo GetImmutableEnumerableCreateRangeMethod(this Type type,
183173
return null!;
184174
}
185175

186-
[DynamicDependency(CreateRangeMethodNameForDictionary, ImmutableDictionaryTypeName, ImmutableCollectionsAssembly)]
187-
[DynamicDependency(CreateRangeMethodNameForDictionary, ImmutableSortedDictionaryTypeName, ImmutableCollectionsAssembly)]
188-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2075:UnrecognizedReflectionPattern",
189-
Justification = "The CreateRange method is preserved by the DynamicDependency attribute.")]
190-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2060:MakeGenericMethod",
191-
Justification = "Immutable collections don't have trimming annotations.")]
176+
[RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)]
192177
public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type, Type keyType, Type valueType)
193178
{
194179
Type? constructingType = GetImmutableDictionaryConstructingType(type);
@@ -211,9 +196,7 @@ public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type,
211196
return null!;
212197
}
213198

214-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
215-
Justification = "Json can't take a direct dependency on Collections.Immutable." +
216-
"The types used here will be preserved by the DynamicDependency attributes on the only caller - GetImmutableEnumerableCreateRangeMethod.")]
199+
[RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)]
217200
private static Type? GetImmutableEnumerableConstructingType(Type type)
218201
{
219202
Debug.Assert(type.IsImmutableEnumerableType());
@@ -257,9 +240,7 @@ public static MethodInfo GetImmutableDictionaryCreateRangeMethod(this Type type,
257240
return underlyingType.Assembly.GetType(constructingTypeName);
258241
}
259242

260-
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
261-
Justification = "Json can't take a direct dependency on Collections.Immutable." +
262-
"The types used here will be preserved by the DynamicDependency attributes on the only caller - GetImmutableDictionaryCreateRangeMethod.")]
243+
[RequiresUnreferencedCode(ImmutableConvertersUnreferencedCodeMessage)]
263244
private static Type? GetImmutableDictionaryConstructingType(Type type)
264245
{
265246
Debug.Assert(type.IsImmutableDictionaryType());

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableDictionaryOfTKeyTValueConverter.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Text.Json.Serialization.Metadata;
67

78
namespace System.Text.Json.Serialization.Converters
@@ -11,6 +12,11 @@ internal sealed class ImmutableDictionaryOfTKeyTValueConverter<TCollection, TKey
1112
where TCollection : IReadOnlyDictionary<TKey, TValue>
1213
where TKey : notnull
1314
{
15+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
16+
public ImmutableDictionaryOfTKeyTValueConverter()
17+
{
18+
}
19+
1420
protected override void Add(TKey key, in TValue value, JsonSerializerOptions options, ref ReadStack state)
1521
{
1622
((Dictionary<TKey, TValue>)state.Current.ReturnValue!)[key] = value;
@@ -23,6 +29,8 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
2329
state.Current.ReturnValue = new Dictionary<TKey, TValue>();
2430
}
2531

32+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
33+
Justification = "The ctor is marked RequiresUnreferencedCode.")]
2634
protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
2735
{
2836
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Converters/Collection/ImmutableEnumerableOfTConverter.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System.Collections.Generic;
5+
using System.Diagnostics.CodeAnalysis;
56
using System.Text.Json.Serialization.Metadata;
67

78
namespace System.Text.Json.Serialization.Converters
@@ -10,6 +11,11 @@ internal sealed class ImmutableEnumerableOfTConverter<TCollection, TElement>
1011
: IEnumerableDefaultConverter<TCollection, TElement>
1112
where TCollection : IEnumerable<TElement>
1213
{
14+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
15+
public ImmutableEnumerableOfTConverter()
16+
{
17+
}
18+
1319
protected override void Add(in TElement value, ref ReadStack state)
1420
{
1521
((List<TElement>)state.Current.ReturnValue!).Add(value);
@@ -22,6 +28,8 @@ protected override void CreateCollection(ref Utf8JsonReader reader, ref ReadStac
2228
state.Current.ReturnValue = new List<TElement>();
2329
}
2430

31+
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode",
32+
Justification = "The ctor is marked RequiresUnreferencedCode.")]
2533
protected override void ConvertCollection(ref ReadStack state, JsonSerializerOptions options)
2634
{
2735
JsonTypeInfo typeInfo = state.Current.JsonTypeInfo;

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/MemberAccessor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@ public abstract JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, T
1919

2020
public abstract Action<TCollection, object?> CreateAddMethodDelegate<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] TCollection>();
2121

22+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
2223
public abstract Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>();
2324

25+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
2426
public abstract Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>();
2527

2628
public abstract Func<object, TProperty> CreatePropertyGetter<TProperty>(PropertyInfo propertyInfo);

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionEmitMemberAccessor.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,12 @@ private static DynamicMethod CreateAddMethodDelegate(
172172
return dynamicMethod;
173173
}
174174

175+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
175176
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>() =>
176177
CreateDelegate<Func<IEnumerable<TElement>, TCollection>>(
177178
CreateImmutableEnumerableCreateRangeDelegate(typeof(TCollection), typeof(TElement), typeof(IEnumerable<TElement>)));
178179

180+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
179181
private static DynamicMethod CreateImmutableEnumerableCreateRangeDelegate(Type collectionType, Type elementType, Type enumerableType)
180182
{
181183
MethodInfo realMethod = collectionType.GetImmutableEnumerableCreateRangeMethod(elementType);
@@ -196,10 +198,12 @@ private static DynamicMethod CreateImmutableEnumerableCreateRangeDelegate(Type c
196198
return dynamicMethod;
197199
}
198200

201+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
199202
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>() =>
200203
CreateDelegate<Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection>>(
201204
CreateImmutableDictionaryCreateRangeDelegate(typeof(TCollection), typeof(TKey), typeof(TValue), typeof(IEnumerable<KeyValuePair<TKey, TValue>>)));
202205

206+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
203207
private static DynamicMethod CreateImmutableDictionaryCreateRangeDelegate(Type collectionType, Type keyType, Type valueType, Type enumerableType)
204208
{
205209
MethodInfo realMethod = collectionType.GetImmutableDictionaryCreateRangeMethod(keyType, valueType);

src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/ReflectionMemberAccessor.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,13 +124,15 @@ public override JsonTypeInfo.ParameterizedConstructorDelegate<T, TArg0, TArg1, T
124124
};
125125
}
126126

127+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
127128
public override Func<IEnumerable<TElement>, TCollection> CreateImmutableEnumerableCreateRangeDelegate<TCollection, TElement>()
128129
{
129130
MethodInfo createRange = typeof(TCollection).GetImmutableEnumerableCreateRangeMethod(typeof(TElement));
130131
return (Func<IEnumerable<TElement>, TCollection>)createRange.CreateDelegate(
131132
typeof(Func<IEnumerable<TElement>, TCollection>));
132133
}
133134

135+
[RequiresUnreferencedCode(IEnumerableConverterFactoryHelpers.ImmutableConvertersUnreferencedCodeMessage)]
134136
public override Func<IEnumerable<KeyValuePair<TKey, TValue>>, TCollection> CreateImmutableDictionaryCreateRangeDelegate<TCollection, TKey, TValue>()
135137
{
136138
MethodInfo createRange = typeof(TCollection).GetImmutableDictionaryCreateRangeMethod(typeof(TKey), typeof(TValue));

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/IImmutableDictionary.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/IImmutableList.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/IImmutableQueue.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/IImmutableSet.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/IImmutableStack.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/ImmutableArray.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/ImmutableDictionary.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

src/libraries/System.Text.Json/tests/System.Text.Json.Tests/TrimmingTests/Collections/ImmutableHashSet.cs

Lines changed: 0 additions & 26 deletions
This file was deleted.

0 commit comments

Comments
 (0)