diff --git a/Directory.Build.props b/Directory.Build.props index 48a18c427..2fb1f6a36 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -25,7 +25,7 @@ - + @@ -39,7 +39,7 @@ $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - + diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 713592b83..81b40fd16 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -34,6 +34,10 @@ jobs: # Test solution # + # Run .NET 8 unit tests + - script: dotnet test --no-build -c $(Build.Configuration) -f net8.0 -l "trx;LogFileName=VSTestResults_net8.0.trx" + displayName: Run .NET 8 unit tests + # Run .NET 7 unit tests - script: dotnet test --no-build -c $(Build.Configuration) -f net7.0 -l "trx;LogFileName=VSTestResults_net7.0.trx" displayName: Run .NET 7 unit tests diff --git a/build/Community.Toolkit.Common.props b/build/Community.Toolkit.Common.props index a43ae645d..4d0ec4379 100644 --- a/build/Community.Toolkit.Common.props +++ b/build/Community.Toolkit.Common.props @@ -20,7 +20,7 @@ true - 11.0 + 12.0 enable $(Product) Asset diff --git a/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj b/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj index f2d7cb70b..ddf81b30c 100644 --- a/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj +++ b/src/CommunityToolkit.Common/CommunityToolkit.Common.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1;net6.0 + netstandard2.0;netstandard2.1;net6.0;net8.0 diff --git a/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj b/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj index 6cd510f16..fec859625 100644 --- a/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj +++ b/src/CommunityToolkit.Diagnostics/CommunityToolkit.Diagnostics.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1;net6.0 + netstandard2.0;netstandard2.1;net6.0;net8.0 diff --git a/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj b/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj index bfc0e6bbe..2cf7770af 100644 --- a/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj +++ b/src/CommunityToolkit.HighPerformance/CommunityToolkit.HighPerformance.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1;net6.0;net7.0 + netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 diff --git a/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs index 98fe1f38b..f4a6cac3a 100644 --- a/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs +++ b/src/CommunityToolkit.HighPerformance/Extensions/NullableExtensions.cs @@ -48,7 +48,7 @@ public static ref T DangerousGetValueOrDefaultReference(this ref T? value) /// The type of the underlying value. /// The . /// A reference to the value of the input instance, or a reference. - /// The returned reference can be tested for using . + /// The returned reference can be tested for using . [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ref T DangerousGetValueOrNullReference(ref this T? value) where T : struct diff --git a/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs index 0e78fc341..dfae52a13 100644 --- a/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs +++ b/src/CommunityToolkit.HighPerformance/Extensions/ReadOnlySpanExtensions.cs @@ -213,7 +213,7 @@ public static ReadOnlySpan2D AsSpan2D(this ReadOnlySpan span, int offse public static unsafe int IndexOf(this ReadOnlySpan span, in T value) { ref T r0 = ref MemoryMarshal.GetReference(span); - ref T r1 = ref Unsafe.AsRef(value); + ref T r1 = ref Unsafe.AsRef(in value); IntPtr byteOffset = Unsafe.ByteOffset(ref r0, ref r1); nint elementOffset = byteOffset / (nint)(uint)sizeof(T); diff --git a/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs index a4808e15b..500c65001 100644 --- a/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs +++ b/src/CommunityToolkit.HighPerformance/Extensions/StreamExtensions.cs @@ -275,7 +275,7 @@ public static unsafe void Write(this Stream stream, in T value) where T : unmanaged { #if NETSTANDARD2_1_OR_GREATER - ref T r0 = ref Unsafe.AsRef(value); + ref T r0 = ref Unsafe.AsRef(in value); ref byte r1 = ref Unsafe.As(ref r0); int length = sizeof(T); diff --git a/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs b/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs index a3260c5bd..b2875637c 100644 --- a/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs +++ b/src/CommunityToolkit.HighPerformance/Extensions/StringExtensions.cs @@ -27,7 +27,7 @@ public static class StringExtensions public static ref char DangerousGetReference(this string text) { #if NET6_0_OR_GREATER - return ref Unsafe.AsRef(text.GetPinnableReference()); + return ref Unsafe.AsRef(in text.GetPinnableReference()); #else return ref MemoryMarshal.GetReference(text.AsSpan()); #endif @@ -44,7 +44,7 @@ public static ref char DangerousGetReference(this string text) public static ref char DangerousGetReferenceAt(this string text, int i) { #if NET6_0_OR_GREATER - ref char r0 = ref Unsafe.AsRef(text.GetPinnableReference()); + ref char r0 = ref Unsafe.AsRef(in text.GetPinnableReference()); #else ref char r0 = ref MemoryMarshal.GetReference(text.AsSpan()); #endif diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs index 5a8fe49d5..4ea95a236 100644 --- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs +++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction.cs @@ -172,7 +172,7 @@ public static void For(int start, int end, in TAction action, int minim { for (int i = start; i < end; i++) { - Unsafe.AsRef(action).Invoke(i); + Unsafe.AsRef(in action).Invoke(i); } return; @@ -225,7 +225,7 @@ public void Invoke(int i) for (int j = low; j < stop; j++) { - Unsafe.AsRef(this.action).Invoke(j); + Unsafe.AsRef(in this.action).Invoke(j); } } } diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs index 6a16c8ca1..66009ea7b 100644 --- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs +++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.For.IAction2D.cs @@ -257,7 +257,7 @@ public static void For2D(int top, int bottom, int left, int right, in T { for (int x = left; x < right; x++) { - Unsafe.AsRef(action).Invoke(y, x); + Unsafe.AsRef(in action).Invoke(y, x); } } @@ -319,7 +319,7 @@ public void Invoke(int i) { for (int x = this.startX; x < this.endX; x++) { - Unsafe.AsRef(this.action).Invoke(y, x); + Unsafe.AsRef(in this.action).Invoke(y, x); } } } diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs index 7d4ae7a7c..f0e4790af 100644 --- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs +++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction.cs @@ -91,7 +91,7 @@ public static void ForEach(ReadOnlyMemory memory, in TAct { foreach (TItem? item in memory.Span) { - Unsafe.AsRef(action).Invoke(item); + Unsafe.AsRef(in action).Invoke(item); } return; @@ -144,7 +144,7 @@ public void Invoke(int i) while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) { - Unsafe.AsRef(this.action).Invoke(in rStart); + Unsafe.AsRef(in this.action).Invoke(in rStart); rStart = ref Unsafe.Add(ref rStart, 1); } diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs index e10361c16..a600c529c 100644 --- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs +++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IInAction2D.cs @@ -144,7 +144,7 @@ public void Invoke(int i) while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) { - Unsafe.AsRef(this.action).Invoke(in rStart); + Unsafe.AsRef(in this.action).Invoke(in rStart); rStart = ref Unsafe.Add(ref rStart, 1); } diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs index ca990f4fc..86ed6290f 100644 --- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs +++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction.cs @@ -91,7 +91,7 @@ public static void ForEach(Memory memory, in TAction acti { foreach (ref TItem item in memory.Span) { - Unsafe.AsRef(action).Invoke(ref item); + Unsafe.AsRef(in action).Invoke(ref item); } return; @@ -144,7 +144,7 @@ public void Invoke(int i) while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) { - Unsafe.AsRef(this.action).Invoke(ref rStart); + Unsafe.AsRef(in this.action).Invoke(ref rStart); rStart = ref Unsafe.Add(ref rStart, 1); } diff --git a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs index 4a6bfc28c..799f26d69 100644 --- a/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs +++ b/src/CommunityToolkit.HighPerformance/Helpers/ParallelHelper.ForEach.IRefAction2D.cs @@ -151,7 +151,7 @@ public void Invoke(int i) while (Unsafe.IsAddressLessThan(ref rStart, ref rEnd)) { - Unsafe.AsRef(this.action).Invoke(ref rStart); + Unsafe.AsRef(in this.action).Invoke(ref rStart); rStart = ref Unsafe.Add(ref rStart, 1); } diff --git a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj index 272f56c7f..e6c7d8be2 100644 --- a/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj +++ b/src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj @@ -1,7 +1,7 @@ - netstandard2.0;netstandard2.1;net6.0 + netstandard2.0;netstandard2.1;net6.0;net8.0 @@ -22,7 +22,7 @@ - + @@ -55,6 +55,7 @@ System.Diagnostics.CodeAnalysis.NotNullAttribute; System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute; System.Diagnostics.CodeAnalysis.NotNullWhenAttribute; + System.Diagnostics.CodeAnalysis.RequiresDynamicCodeAttribute; System.Diagnostics.CodeAnalysis.RequiresUnreferencedCodeAttribute; System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute; System.Runtime.CompilerServices.CallerArgumentExpressionAttribute; @@ -65,7 +66,7 @@ - + diff --git a/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs b/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs index 63b28441b..5cd9433e6 100644 --- a/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs +++ b/src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs @@ -64,6 +64,12 @@ public bool IsActive "If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " + "path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type. " + "Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")] + [RequiresDynamicCode( + "When this property is set to true, the OnActivated() method will be invoked, which will register all necessary message handlers for this recipient. " + + "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " + + "If that is present, the method is AOT safe, as the only methods being invoked to register the messages will be the ones produced by the source generator. " + + "If it isn't, this method will need to dynamically create the generic methods to register messages, which might not be available at runtime. " + + "Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")] set { if (SetProperty(ref this.isActive, value, true)) @@ -96,6 +102,11 @@ public bool IsActive "If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " + "path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type. " + "Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")] + [RequiresDynamicCode( + "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " + + "If that is present, the method is AOT safe, as the only methods being invoked to register the messages will be the ones produced by the source generator. " + + "If it isn't, this method will need to dynamically create the generic methods to register messages, which might not be available at runtime. " + + "Alternatively, OnActivated() can be manually overwritten, and registration can be done individually for each required message for this recipient.")] protected virtual void OnActivated() { Messenger.RegisterAll(this); diff --git a/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs b/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs index f6f038166..6febfb2e1 100644 --- a/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs +++ b/src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs @@ -90,6 +90,10 @@ public static bool IsRegistered(this IMessenger messenger, object reci "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " + "If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " + "path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type.")] + [RequiresDynamicCode( + "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " + + "If that is present, the method is AOT safe, as the only methods being invoked to register the messages will be the ones produced by the source generator. " + + "If it isn't, this method will need to dynamically create the generic methods to register messages, which might not be available at runtime.")] public static void RegisterAll(this IMessenger messenger, object recipient) { ArgumentNullException.ThrowIfNull(messenger); @@ -113,7 +117,7 @@ public static void RegisterAll(this IMessenger messenger, object recipient) // Try to get the cached delegate, if the generator has run correctly Action? registrationAction = DiscoveredRecipients.RegistrationMethods.GetValue( recipient.GetType(), - [RequiresUnreferencedCode("The type of the current instance cannot be statically discovered.")] static (t) => LoadRegistrationMethodsForType(t)); + LoadRegistrationMethodsForType); if (registrationAction is not null) { @@ -144,6 +148,7 @@ public static void RegisterAll(this IMessenger messenger, object recipient) "This method requires the generated CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions type not to be removed to use the fast path. " + "If this type is removed by the linker, or if the target recipient was created dynamically and was missed by the source generator, a slower fallback " + "path using a compiled LINQ expression will be used. This will have more overhead in the first invocation of this method for any given recipient type.")] + [RequiresDynamicCode("The generic methods to register messages might not be available at runtime.")] public static void RegisterAll(this IMessenger messenger, object recipient, TToken token) where TToken : IEquatable { @@ -156,6 +161,7 @@ public static void RegisterAll(this IMessenger messenger, object recipie // target recipient type, and just invoke it to get the delegate to cache and use later. // In this case we also need to create a generic instantiation of the target method first. [RequiresUnreferencedCode("The type of the current instance cannot be statically discovered.")] + [RequiresDynamicCode("The generic methods to register messages might not be available at runtime.")] static Action LoadRegistrationMethodsForType(Type recipientType) { if (recipientType.Assembly.GetType("CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions") is Type extensionsType && @@ -173,6 +179,7 @@ static Action LoadRegistrationMethodsForType(Type re // This method is only invoked once per recipient type and token type, so we're not // worried about making it super efficient, and we can use the LINQ code for clarity. // The LINQ codegen bloat is not really important for the same reason. + [RequiresDynamicCode("The generic methods to register messages might not be available at runtime.")] static Action LoadRegistrationMethodsForTypeFallback(Type recipientType) { // Get the collection of validation methods @@ -231,7 +238,7 @@ from registrationMethod in registrationMethods // For more info on this, see the related issue at https://github.com/dotnet/roslyn/issues/5835. Action registrationAction = DiscoveredRecipients.RegistrationMethods.GetValue( recipient.GetType(), - [RequiresUnreferencedCode("The type of the current instance cannot be statically discovered.")] static (t) => LoadRegistrationMethodsForType(t)); + LoadRegistrationMethodsForType); // Invoke the cached delegate to actually execute the message registration registrationAction(messenger, recipient, token); diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 882db66f3..486e75379 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,7 +3,7 @@ - + all build; analyzers diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index b070ed1b8..23f65c476 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -3,18 +3,23 @@ - + NETSTANDARD2_1_OR_GREATER - + true true true true + + + true + +