Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove NS1.x assets from building and packaging #53283

Merged
merged 3 commits into from
May 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/libraries/Common/src/System/CodeDom/CodeObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
using System.Collections;
using System.Collections.Specialized;

#if !FEATURE_SERIALIZATION
#if CODEDOM
namespace System.CodeDom
#else
namespace System.Runtime.Serialization
#endif
{
#if !FEATURE_SERIALIZATION
#if CODEDOM
public class CodeObject
#else
internal class CodeObject
Expand Down
8 changes: 4 additions & 4 deletions src/libraries/Common/src/System/CodeDom/CodeTypeReference.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@
using System.Diagnostics;
using System.Globalization;

#if !FEATURE_SERIALIZATION
#if CODEDOM
namespace System.CodeDom
#else
namespace System.Runtime.Serialization
#endif
{
[Flags]
#if !FEATURE_SERIALIZATION
#if CODEDOM
public enum CodeTypeReferenceOptions
#else
internal enum CodeTypeReferenceOptions
Expand All @@ -22,7 +22,7 @@ internal enum CodeTypeReferenceOptions
GenericTypeParameter = 0x00000002
}

#if !FEATURE_SERIALIZATION
#if CODEDOM
public class CodeTypeReference : CodeObject
#else
internal sealed class CodeTypeReference : CodeObject
Expand Down Expand Up @@ -281,7 +281,7 @@ public CodeTypeReference(string typeName, params CodeTypeReference[] typeArgumen
}
}

#if !FEATURE_SERIALIZATION
#if CODEDOM
public CodeTypeReference(CodeTypeParameter typeParameter) :
this(typeParameter?.Name)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

using System.Collections;

#if !FEATURE_SERIALIZATION
#if CODEDOM
namespace System.CodeDom
#else
namespace System.Runtime.Serialization
#endif
{
#if !FEATURE_SERIALIZATION
#if CODEDOM
public class CodeTypeReferenceCollection : CollectionBase
#else
internal sealed class CodeTypeReferenceCollection : CollectionBase
Expand Down
2 changes: 1 addition & 1 deletion src/libraries/Common/src/System/HexConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ public static void EncodeToUtf16(ReadOnlySpan<byte> bytes, Span<char> chars, Cas
#endif
public static unsafe string ToString(ReadOnlySpan<byte> bytes, Casing casing = Casing.Upper)
{
#if NETFRAMEWORK || NETSTANDARD1_0 || NETSTANDARD1_3 || NETSTANDARD2_0
#if NETFRAMEWORK || NETSTANDARD2_0
Span<char> result = stackalloc char[0];
if (bytes.Length > 16)
{
Expand Down
4 changes: 2 additions & 2 deletions src/libraries/Common/src/System/SR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace System
{
internal static partial class SR
{
#if (!NETSTANDARD1_0 && !NETSTANDARD1_1 && !NET45) // AppContext is not supported on < NetStandard1.3 or < .NET Framework 4.5
#if !NET45 // AppContext is not supported on < .NET Framework 4.5
private static readonly bool s_usingResourceKeys = AppContext.TryGetSwitch("System.Resources.UseSystemResourceKeys", out bool usingResourceKeys) ? usingResourceKeys : false;
#endif

Expand All @@ -17,7 +17,7 @@ internal static partial class SR
// Native code generators can replace the value this returns based on user input at the time of native code generation.
// The Linker is also capable of replacing the value of this method when the application is being trimmed.
private static bool UsingResourceKeys() =>
#if (!NETSTANDARD1_0 && !NETSTANDARD1_1 && !NET45) // AppContext is not supported on < NetStandard1.3 or < .NET Framework 4.5
#if !NET45 // AppContext is not supported on < .NET Framework 4.5
s_usingResourceKeys;
#else
false;
Expand Down
12 changes: 4 additions & 8 deletions src/libraries/Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -241,14 +241,10 @@
</ItemGroup>
</Target>

<!-- Adds Nullable annotation attributes to netstandard <= 2.0 builds -->
<Choose>
<When Condition="'$(Nullable)' != '' and ($(TargetFramework.StartsWith('netstandard1')) or '$(TargetFramework)' == 'netstandard2.0' or $(TargetFramework.StartsWith('netcoreapp2')) or '$(TargetFrameworkIdentifier)' == '.NETFramework')">
<ItemGroup>
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\NullableAttributes.cs" Link="System\Diagnostics\CodeAnalysis\NullableAttributes.cs" />
</ItemGroup>
</When>
</Choose>
<!-- Adds Nullable annotation attributes to < .NET 5 builds. -->
<ItemGroup Condition="'$(Nullable)' != '' and !$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net5.0'))">
<Compile Include="$(CoreLibSharedDir)System\Diagnostics\CodeAnalysis\NullableAttributes.cs" Link="System\Diagnostics\CodeAnalysis\NullableAttributes.cs" />
</ItemGroup>
ViktorHofer marked this conversation as resolved.
Show resolved Hide resolved

<PropertyGroup>
<ExcludeFromPackage Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', '$(NetCoreAppCurrent)')) and '$(ExcludeCurrentNetCoreAppFromPackage)' == 'true'">true</ExcludeFromPackage>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
<Project Sdk="Microsoft.Build.NoTargets">
<!-- Even though this project doesn't compile, we keep the csproj extension so that the source
package infra globs for .cs files. -->
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<!-- The project doesn't compile anything therefore create the package during build. -->
<GeneratePackageOnBuild Condition="'$(BuildingAnOfficialBuildLeg)' != 'true'">true</GeneratePackageOnBuild>
<IsShipping>false</IsShipping>
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
<!-- This is a source package which includes all .cs files by default. -->
<IsSourcePackage>true</IsSourcePackage>
<!-- This is non-shipping package. -->
<EnablePackageBaselineValidation>false</EnablePackageBaselineValidation>
<PackageDescription>Internal package for sharing Microsoft.Extensions.Hosting.HostFactoryResolver type.</PackageDescription>
</PropertyGroup>

<ItemGroup>
<Content Include="*.cs" PackagePath="contentFiles/cs/netstandard1.0/%(FileName)%(Extension)" BuildAction="Compile" />
</ItemGroup>
</Project>
4 changes: 2 additions & 2 deletions src/libraries/System.CodeDom/src/System.CodeDom.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
</PropertyGroup>
<!-- DesignTimeBuild requires all the TargetFramework Derived Properties to not be present in the first property group. -->
<PropertyGroup>
<IsPartialFacadeAssembly Condition="$(TargetFramework.StartsWith('net4'))">true</IsPartialFacadeAssembly>
<IsPartialFacadeAssembly Condition="'$(TargetFramework)' == 'net461'">true</IsPartialFacadeAssembly>
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<ItemGroup Condition="'$(IsPartialFacadeAssembly)' != 'true'">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious on why choose IsPartialFacadeAssembly over the TargetFramework ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IsPartialFacadeAssembly is more expressive in this case as it indicates that the partial facade assembly doesn't contain any source where as the non partial facade assembly only contains source and no type forwards.

<Compile Include="Microsoft\CSharp\CSharpCodeGenerator.cs" />
<Compile Include="Microsoft\CSharp\CSharpCodeGenerator.PlatformNotSupported.cs" />
<Compile Include="Microsoft\CSharp\CSharpCodeProvider.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.props))" />
<ItemGroup>
<ProjectReference Include="..\src\System.Collections.Immutable.csproj">
<SupportedFramework>net45;netcore45;netcoreapp1.0;wp8;wpa81;$(AllXamarinFrameworks)</SupportedFramework>
<SupportedFramework>net461;netcoreapp2.0;uap10.0.16299;$(AllXamarinFrameworks)</SupportedFramework>
</ProjectReference>

<!-- Since UAP and .NETCoreApp are package based we still want to enable
Expand All @@ -13,6 +13,9 @@
<ValidatePackageSuppression Include="TreatAsOutOfBox">
<Value>.NETCoreApp;UAP</Value>
</ValidatePackageSuppression>

<!-- Exclude TFMs that aren't supported by the package anymore from validation. -->
<ExcludeHarvestedSupportedFramework Include="netcoreapp1.0;netcoreapp1.1;netcore45;netcore451;netcore50;uap10.0;net45;net451;net46;wp8;wpa81" />
</ItemGroup>
<Import Project="$([MSBuild]::GetPathOfFileAbove(Directory.Build.targets))" />
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,8 @@ public static partial class ImmutableArray
public System.Collections.Immutable.ImmutableArray<T> Add(T item) { throw null; }
public System.Collections.Immutable.ImmutableArray<T> AddRange(System.Collections.Generic.IEnumerable<T> items) { throw null; }
public System.Collections.Immutable.ImmutableArray<T> AddRange(System.Collections.Immutable.ImmutableArray<T> items) { throw null; }
#if !NETSTANDARD1_0
Copy link
Member

@stephentoub stephentoub May 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System.Collections.Immutable is widely used, including by Roslyn. Just want to re-confirm there are no known remaining important dependencies on the .NET Standard 1.x assets?

Copy link
Member Author

@ViktorHofer ViktorHofer May 26, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let me provide clarity on the overall communication strategy and then go into specifics for Roslyn.

  1. The change to remove unsupported application tfms (.NETCoreApp < 3.1 and .NETFramework < 4.6.1) and pre netstandard2.0 library assets (.NETStandard < 2.0) was approved by leadership months ago.
  2. The first wave of this effort (removing harvested assets) was presented to Tactics months ago.
  3. In yesterday's meeting I communicated that I'll start with trimming the remaining 25 shipping packages and we agreed on to a) send out a separate mail to Tactics in case people were not present in the meeting b) send out a notice to our first party customers via the breaking change notice alias which includes filing a public breaking change issue.
  4. The docs team will then later convert the breaking change issue into a docs page.

In parallel to submitting these PRs (one for .NETStandard, another one for .NETFramework and the last one for .NETCoreApp) I'm filing the breaking change issue to send out the mail to Tactics and the bcn alias. I plan to do that later this week.

In regards to roslyn as a consumer, I found three code pieces where they still consume the netstandard1.x asset of
System.Collections.Immutable and System.Reflection.Metadata. All three projects will need to react to this change by either a) upgrading the tfms or if that's not possible/desirable b) continue referencing the 5.0.0 package even after the 6.0.0 shipped (only for configurations that are affected by this change). Thanks for the feedback, I'll make sure to include the roslyn team in the breaking change notice and will proactively reach out to them.

Customers who try to upgrade these packages to the 6.0.0 version (or to the preview 6 pre-release versions) and still target netstandard1.x, < net461 or < netcoreapp3.1 in their applications will hit an upgrade error in the NuGet Package Explorer or when trying to restore the newer version of the packages i.e. via dotnet restore. The error will inform customers about which packages don't support the tfms in use anymore.

Please let me know if you have further suggestions regarding the handling and communication of the breaking changes.

public System.ReadOnlyMemory<T> AsMemory() { throw null; }
public System.ReadOnlySpan<T> AsSpan() { throw null; }
#endif
public System.Collections.Immutable.ImmutableArray<
#nullable disable
TOther
Expand Down Expand Up @@ -154,9 +152,7 @@ public void CopyTo(T[] destination, int destinationIndex) { }
public System.Collections.Immutable.ImmutableArray<T> Insert(int index, T item) { throw null; }
public System.Collections.Immutable.ImmutableArray<T> InsertRange(int index, System.Collections.Generic.IEnumerable<T> items) { throw null; }
public System.Collections.Immutable.ImmutableArray<T> InsertRange(int index, System.Collections.Immutable.ImmutableArray<T> items) { throw null; }
#if !NETSTANDARD1_0
public ref readonly T ItemRef(int index) { throw null; }
#endif
public int LastIndexOf(T item) { throw null; }
public int LastIndexOf(T item, int startIndex) { throw null; }
public int LastIndexOf(T item, int startIndex, int count) { throw null; }
Expand Down Expand Up @@ -239,9 +235,7 @@ public void CopyTo(T[] array, int index) { }
public int IndexOf(T item, int startIndex, int count) { throw null; }
public int IndexOf(T item, int startIndex, int count, System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
public void Insert(int index, T item) { }
#if !NETSTANDARD1_0
public ref readonly T ItemRef(int index) { throw null; }
#endif
public int LastIndexOf(T item) { throw null; }
public int LastIndexOf(T item, int startIndex) { throw null; }
public int LastIndexOf(T item, int startIndex, int count) { throw null; }
Expand Down Expand Up @@ -423,7 +417,7 @@ public static partial class ImmutableHashSet
public static System.Collections.Immutable.ImmutableHashSet<TSource> ToImmutableHashSet<TSource>(this System.Collections.Immutable.ImmutableHashSet<TSource>.Builder builder) { throw null; }
}

#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !NETSTANDARD2_0 && !NETFRAMEWORK
#if !NETSTANDARD2_0 && !NETFRAMEWORK
public sealed partial class ImmutableHashSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.ISet<T>, System.Collections.Generic.IReadOnlySet<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableSet<T>
#else
public sealed partial class ImmutableHashSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.ISet<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.Immutable.IImmutableSet<T>
Expand Down Expand Up @@ -598,9 +592,7 @@ public void ForEach(System.Action<T> action) { }
public int IndexOf(T item, int index, int count, System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
public System.Collections.Immutable.ImmutableList<T> Insert(int index, T item) { throw null; }
public System.Collections.Immutable.ImmutableList<T> InsertRange(int index, System.Collections.Generic.IEnumerable<T> items) { throw null; }
#if !NETSTANDARD1_0
public ref readonly T ItemRef(int index) { throw null; }
#endif
public int LastIndexOf(T item, int index, int count, System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
public System.Collections.Immutable.ImmutableList<T> Remove(T value) { throw null; }
public System.Collections.Immutable.ImmutableList<T> Remove(T value, System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
Expand Down Expand Up @@ -688,9 +680,7 @@ public void ForEach(System.Action<T> action) { }
public int IndexOf(T item, int index, int count, System.Collections.Generic.IEqualityComparer<T>? equalityComparer) { throw null; }
public void Insert(int index, T item) { }
public void InsertRange(int index, System.Collections.Generic.IEnumerable<T> items) { }
#if !NETSTANDARD1_0
public ref readonly T ItemRef(int index) { throw null; }
#endif
public int LastIndexOf(T item) { throw null; }
public int LastIndexOf(T item, int startIndex) { throw null; }
public int LastIndexOf(T item, int startIndex, int count) { throw null; }
Expand Down Expand Up @@ -747,9 +737,7 @@ internal ImmutableQueue() { }
public System.Collections.Immutable.ImmutableQueue<T> Enqueue(T value) { throw null; }
public System.Collections.Immutable.ImmutableQueue<T>.Enumerator GetEnumerator() { throw null; }
public T Peek() { throw null; }
#if !NETSTANDARD1_0
public ref readonly T PeekRef() { throw null; }
#endif
System.Collections.Generic.IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator() { throw null; }
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { throw null; }
System.Collections.Immutable.IImmutableQueue<T> System.Collections.Immutable.IImmutableQueue<T>.Clear() { throw null; }
Expand Down Expand Up @@ -840,9 +828,7 @@ void System.Collections.IDictionary.Remove(object key) { }
public System.Collections.Immutable.ImmutableSortedDictionary<TKey, TValue>.Builder ToBuilder() { throw null; }
public bool TryGetKey(TKey equalKey, out TKey actualKey) { throw null; }
public bool TryGetValue(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; }
#if !NETSTANDARD1_0
public ref readonly TValue ValueRef(TKey key) { throw null; }
#endif
public System.Collections.Immutable.ImmutableSortedDictionary<TKey, TValue> WithComparers(System.Collections.Generic.IComparer<TKey>? keyComparer) { throw null; }
public System.Collections.Immutable.ImmutableSortedDictionary<TKey, TValue> WithComparers(System.Collections.Generic.IComparer<TKey>? keyComparer, System.Collections.Generic.IEqualityComparer<TValue>? valueComparer) { throw null; }
public sealed partial class Builder : System.Collections.Generic.ICollection<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IDictionary<TKey, TValue>, System.Collections.Generic.IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IReadOnlyCollection<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.Generic.IReadOnlyDictionary<TKey, TValue>, System.Collections.ICollection, System.Collections.IDictionary, System.Collections.IEnumerable
Expand Down Expand Up @@ -888,9 +874,7 @@ void System.Collections.IDictionary.Remove(object key) { }
public System.Collections.Immutable.ImmutableSortedDictionary<TKey, TValue> ToImmutable() { throw null; }
public bool TryGetKey(TKey equalKey, out TKey actualKey) { throw null; }
public bool TryGetValue(TKey key, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TValue value) { throw null; }
#if !NETSTANDARD1_0
public ref readonly TValue ValueRef(TKey key) { throw null; }
#endif
}
[System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
public partial struct Enumerator : System.Collections.Generic.IEnumerator<System.Collections.Generic.KeyValuePair<TKey, TValue>>, System.Collections.IEnumerator, System.IDisposable
Expand Down Expand Up @@ -921,11 +905,11 @@ public static partial class ImmutableSortedSet
public static System.Collections.Immutable.ImmutableSortedSet<TSource> ToImmutableSortedSet<TSource>(this System.Collections.Immutable.ImmutableSortedSet<TSource>.Builder builder) { throw null; }
}

#if !NETSTANDARD1_0 && !NETSTANDARD1_3 && !NETSTANDARD2_0 && !NETFRAMEWORK
#if !NETSTANDARD2_0 && !NETFRAMEWORK
public sealed partial class ImmutableSortedSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.Generic.ISet<T>, System.Collections.Generic.IReadOnlySet<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Immutable.IImmutableSet<T>
#else
#else
public sealed partial class ImmutableSortedSet<T> : System.Collections.Generic.ICollection<T>, System.Collections.Generic.IEnumerable<T>, System.Collections.Generic.IList<T>, System.Collections.Generic.IReadOnlyCollection<T>, System.Collections.Generic.IReadOnlyList<T>, System.Collections.Generic.ISet<T>, System.Collections.ICollection, System.Collections.IEnumerable, System.Collections.IList, System.Collections.Immutable.IImmutableSet<T>
#endif
#endif
{
internal ImmutableSortedSet() { }
public static readonly System.Collections.Immutable.ImmutableSortedSet<T> Empty;
Expand Down Expand Up @@ -953,9 +937,7 @@ internal ImmutableSortedSet() { }
public bool IsProperSupersetOf(System.Collections.Generic.IEnumerable<T> other) { throw null; }
public bool IsSubsetOf(System.Collections.Generic.IEnumerable<T> other) { throw null; }
public bool IsSupersetOf(System.Collections.Generic.IEnumerable<T> other) { throw null; }
#if !NETSTANDARD1_0
public ref readonly T ItemRef(int index) { throw null; }
#endif
public bool Overlaps(System.Collections.Generic.IEnumerable<T> other) { throw null; }
public System.Collections.Immutable.ImmutableSortedSet<T> Remove(T value) { throw null; }
public System.Collections.Generic.IEnumerable<T> Reverse() { throw null; }
Expand Down Expand Up @@ -1014,9 +996,7 @@ public void IntersectWith(System.Collections.Generic.IEnumerable<T> other) { }
public bool IsProperSupersetOf(System.Collections.Generic.IEnumerable<T> other) { throw null; }
public bool IsSubsetOf(System.Collections.Generic.IEnumerable<T> other) { throw null; }
public bool IsSupersetOf(System.Collections.Generic.IEnumerable<T> other) { throw null; }
#if !NETSTANDARD1_0
public ref readonly T ItemRef(int index) { throw null; }
#endif
public bool Overlaps(System.Collections.Generic.IEnumerable<T> other) { throw null; }
public bool Remove(T item) { throw null; }
public System.Collections.Generic.IEnumerable<T> Reverse() { throw null; }
Expand Down Expand Up @@ -1059,9 +1039,7 @@ internal ImmutableStack() { }
public System.Collections.Immutable.ImmutableStack<T> Clear() { throw null; }
public System.Collections.Immutable.ImmutableStack<T>.Enumerator GetEnumerator() { throw null; }
public T Peek() { throw null; }
#if !NETSTANDARD1_0
public ref readonly T PeekRef() { throw null; }
#endif
public System.Collections.Immutable.ImmutableStack<T> Pop() { throw null; }
public System.Collections.Immutable.ImmutableStack<T> Pop(out T value) { throw null; }
public System.Collections.Immutable.ImmutableStack<T> Push(T value) { throw null; }
Expand Down
Loading