Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
Add AOT annotations to the MVVM Toolkit
  • Loading branch information
Sergio0694 committed Jan 20, 2024
commit a181f0cef7eaf47a889d10a6e8515d7300b35512
1 change: 1 addition & 0 deletions src/CommunityToolkit.Mvvm/CommunityToolkit.Mvvm.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
11 changes: 11 additions & 0 deletions src/CommunityToolkit.Mvvm/ComponentModel/ObservableRecipient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions src/CommunityToolkit.Mvvm/Messaging/IMessengerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,10 @@ public static bool IsRegistered<TMessage>(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);
Expand All @@ -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<IMessenger, object>? 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)
{
Expand Down Expand Up @@ -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<TToken>(this IMessenger messenger, object recipient, TToken token)
where TToken : IEquatable<TToken>
{
Expand All @@ -156,6 +161,7 @@ public static void RegisterAll<TToken>(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<IMessenger, object, TToken> LoadRegistrationMethodsForType(Type recipientType)
{
if (recipientType.Assembly.GetType("CommunityToolkit.Mvvm.Messaging.__Internals.__IMessengerExtensions") is Type extensionsType &&
Expand All @@ -173,6 +179,7 @@ static Action<IMessenger, object, TToken> 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<IMessenger, object, TToken> LoadRegistrationMethodsForTypeFallback(Type recipientType)
{
// Get the collection of validation methods
Expand Down Expand Up @@ -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<IMessenger, object, TToken> registrationAction = DiscoveredRecipients<TToken>.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);
Expand Down