Présentation des avertissements AOT

Lors de la publication de votre application comme application AOA native, le processus de génération génère tout le code natif et les structures de données nécessaires pour prendre en charge l’application au moment de l’exécution. Ceci diffère des déploiements non natifs, qui exécutent l’application à partir de formats qui décrivent l’application en termes abstraits (un programme pour une machine virtuelle) et créent des représentations natives à la demande au moment de l’exécution.

Les représentations abstraites des parties de programme n’ont pas de mappage un-à-un à la représentation native. Par exemple, la description abstraite de la méthode générique List<T>.Add est mappée à des corps de méthode natifs potentiellement infinis qui doivent être spécialisés pour le T donné (par exemple List<int>.Add et List<double>.Add).

Comme la relation entre le code abstrait et le code natif n’est pas un-à-un, le processus de build doit créer une liste complète des corps de code natif et des structures de données au moment de la build. Il peut être difficile de créer cette liste au moment de la build pour certaines API .NET. Si l’API est utilisée d’une manière qui n’était pas prévue au moment de la build, une exception est levée au moment de l’exécution.

Pour empêcher les modifications du comportement lors du déploiement en tant qu’application AOA native, le SDK .NET fournit une analyse statique de la compatibilité AOA par le biais des « avertissements AOA ». Les avertissements AOA sont générés lorsque la build trouve du code qui peut ne pas être compatible avec AOA. Le code qui n’est pas compatible avec AOA peut produire des changements de comportement ou même des plantages dans une application après sa génération en tant qu’application AOA native. Dans l’idéal, toutes les applications qui utilisent l’AOA natif ne doivent avoir aucun avertissement AOA. S’il existe des avertissements AOA, vérifiez qu’il n’y a aucun changement de comportement en testant minutieusement votre application après sa génération en tant qu’application AOA native.

Exemples d’avertissements AOT

Pour la plupart du code C#, il est simple de déterminer quel code natif doit être généré. Le compilateur natif peut parcourir les corps des méthodes et rechercher le code natif et les structures de données faisant l’objet d’un accès. Malheureusement, certaines fonctionnalités, comme la réflexion, présentent un problème important. Considérez le code suivant :

Type t = typeof(int);
while (true)
{
    t = typeof(GenericType<>).MakeGenericType(t);
    Console.WriteLine(Activator.CreateInstance(t));
}

struct GenericType<T> { }

Bien que le programme ci-dessus ne soit pas très utile, il représente un cas extrême qui nécessite la création d’un nombre infini de types génériques lors de la génération de l’application en tant qu’application AOA native. Sans l’AOA natif, le programme s’exécuterait jusqu’à ce qu’il manque de mémoire. Avec l’AOA natif, nous ne pourrions même pas le générer si nous devions générer tous les types nécessaires (leur nombre est infini).

Dans ce cas, la build AOA native émet l’avertissement suivant sur la ligne MakeGenericType :

AOT analysis warning IL3050: Program.<Main>$(String[]): Using member 'System.Type.MakeGenericType(Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling. The native code for this instantiation might not be available at runtime.

Au moment de l’exécution, l’application va lever une exception depuis l’appel de MakeGenericType.

Réagir aux avertissements AOT

Les avertissements AOA sont destinés à donner de la prévisibilité aux builds AOA natives. La majorité des avertissements AOT concernent une exception d’exécution possible dans les situations où du code natif n’a pas été généré pour prendre en charge le scénario. La catégorie la plus étendue est RequiresDynamicCodeAttribute.

RequiresDynamicCode

RequiresDynamicCodeAttribute est simple et étendu : c’est un attribut qui signifie que le membre a été annoté comme étant incompatible avec AOT. Cette annotation signifie que le membre peut utiliser la réflexion ou un autre mécanisme pour créer du code natif au moment de l’exécution. Cet attribut est utilisé quand le code n’est fondamentalement pas compatible avec AOT ou que la dépendance native est trop complexe pour être prédite statiquement au moment de la build. Ceci est souvent vrai pour les méthodes qui utilisent l’API Type.MakeGenericType, l’émission de réflexion ou d’autres technologies de génération de code au moment de l’exécution. Le code ci-après présente un exemple.

[RequiresDynamicCode("Use 'MethodFriendlyToAot' instead")]
void MethodWithReflectionEmit() { ... }

void TestMethod()
{
    // IL3050: Using method 'MethodWithReflectionEmit' which has 'RequiresDynamicCodeAttribute'
    // can break functionality when AOT compiling. Use 'MethodFriendlyToAot' instead.
    MethodWithReflectionEmit();
}

Il n’existe pas beaucoup de solutions de contournement pour RequiresDynamicCode. La meilleure solution est d’éviter d’appeler la méthode lors de la génération en tant qu’application AOA native et à utiliser quelque chose d’autre qui est compatible avec AOA. Si vous écrivez une bibliothèque et qu’il n’est pas sous votre contrôle d’appeler ou non la méthode, vous pouvez aussi ajouter RequiresDynamicCode à votre propre méthode. Ceci va annoter votre méthode comme n’étant pas compatible avec AOT. L’ajout de RequiresDynamicCode fait que tous les avertissements AOT dans la méthode annotée ne seront pas émis, mais produit un avertissement chaque fois que quelqu’un d’autre l’appelle. C’est pourquoi il est surtout utile pour les auteurs de bibliothèques de « remonter » l’avertissement vers une API publique.

Si vous pouvez déterminer d’une manière ou d’une autre que l’appel est sûr et que tout le code natif sera disponible au moment de l’exécution, vous pouvez également supprimer l’avertissement en utilisant UnconditionalSuppressMessageAttribute. Par exemple :

[RequiresDynamicCode("Use 'MethodFriendlyToAot' instead")]
void MethodWithReflectionEmit() { ... }

[UnconditionalSuppressMessage("Aot", "IL3050:RequiresDynamicCode",
    Justification = "The unfriendly method is not reachable with AOT")]
void TestMethod()
{
    If (RuntimeFeature.IsDynamicCodeSupported)
        MethodWithReflectionEmit(); // warning suppressed
}

UnconditionalSuppressMessage est comme SuppressMessage, mais il peut être vu par publish et par d’autres outils post-build. Les directives SuppressMessage et #pragma sont présentes seulement dans la source, de sorte qu’elles ne peuvent pas être utilisées pour que les avertissements de la build ne soient pas émis.

Attention

Soyez prudent quand vous supprimez des avertissements AOT. L’appel peut être compatible avec AOT maintenant, mais à mesure que vous mettez à jour votre code, cela peut changer et vous risquez d’oublier de passer en revue toutes les suppressions.