Architecture du générateur ABI
Statut documentaire : architecture — voir Maturité et preuves.
Rôle
Le générateur transforme la surface Runtime explicitement publiée en un contrat d'interopérabilité vérifiable, puis en bindings idiomatiques pour les écosystèmes supportés.
Il ne copie pas les classes internes du moteur et ne publie pas leur disposition mémoire. Sa responsabilité est de produire une projection publique stable.
flowchart LR
PUB[Surface Runtime publiée] --> MODEL[Modèle de publication canonique]
MODEL --> MANIFEST[Manifest ABI]
MODEL --> NATIVE[Exports ABI locaux]
MODEL --> DOTNET[Binding .NET]
MODEL --> FUTURE[Bindings futurs]
MANIFEST --> TESTS[Tests de contrat]
Frontière de certification actuelle
Le modèle du générateur est volontairement plus large que l'executor générique actuellement certifié dans le moteur. Dans les sources actuelles, le vocabulaire de manifeste décrit déjà strings, arrays, records, callbacks, bytes, entiers larges et flottants, tandis que le chemin natif générique est actuellement testé de bout en bout pour i32, pointeur, handle et les résultats void.
Les sections ci-dessous sur tableaux, callbacks, métadonnées async/cancellation et projections scalaires riches décrivent donc l'architecture cible de publication/génération, et non une promesse que chaque type est déjà exécutable via le marshaller ABI générique actuel. Voir Marshalling et types de valeurs pour le sous-ensemble certifié.
Pipeline recommandé
1. Découverte de la surface publiée
Le générateur ne doit considérer que les types et membres explicitement destinés à l'interopérabilité. Une classe interne ne devient jamais publique par simple découverte réflexive.
Chaque membre publié doit fournir suffisamment d'information pour déterminer :
- son nom public ;
- son identifiant stable ;
- ses paramètres ;
- sa valeur de retour ;
- sa nullabilité ;
- son ownership ;
- ses capacités async/cancellation ;
- ses types imbriqués ou éléments de collection.
2. Construction d'un modèle canonique
Avant de produire du C#, du Rust ou un export natif, le générateur construit un modèle indépendant du langage.
Cette étape empêche les conventions d'un langage d'implémentation de fuir dans les SDK publics et garantit qu'un même contrat peut être projeté vers plusieurs écosystèmes.
3. Fermeture des types publics
Tous les types accessibles depuis une signature publiée doivent eux-mêmes posséder une projection publique valide.
Une génération correcte a deux issues possibles :
- la signature complète est projetée ;
- la génération échoue avec un diagnostic précis.
Un pointeur opaque utilisé par défaut pour masquer un type inconnu n'est pas une solution acceptable pour une API publique.
4. Génération du manifest
Le manifest est le contrat machine-readable de la publication. Il doit être utilisable indépendamment du code C# généré.
Exemple conceptuel :
{
"bindingVersion": "2",
"abiVersion": "3",
"capabilities": ["object-arrays", "callbacks"],
"types": [
{
"typeId": 1201,
"publicName": "Worker",
"members": []
}
]
}
Les valeurs ci-dessus sont illustratives ; les identifiants effectifs proviennent du manifest généré pour une release donnée.
5. Génération des exports locaux
La couche locale traduit le contrat canonique en opérations utilisables par le runtime client natif. Elle doit préserver :
- identité et affinité du runtime ;
- descripteurs de types ;
- ownership ;
- erreurs structurées ;
- callbacks ;
- tableaux et collections supportés.
Les détails de packing restent internes au binding.
6. Projection par écosystème
Chaque backend applique les conventions de son langage sans changer le sens du contrat.
Pour .NET :
- types publics en PascalCase ;
- paramètres et variables locales en camelCase ;
IDisposablepour les wrappers propriétaires ;CancellationTokenuniquement lorsque l'annulation est réellement supportée ;- suffixe
Asyncuniquement pour des opérations véritablement asynchrones.
Les futurs bindings devront suivre le même principe : idiomatiques au niveau source, identiques au niveau sémantique.
Support des collections
Le contrat cible doit traiter uniformément les tableaux unidimensionnels de valeurs primitives, chaînes et objets publiés.
Une collection d'objets n'est pas un simple tableau de handles : chaque élément conserve son identité Runtime, son affinité et son ownership. La projection doit reconstruire le wrapper public approprié pour chaque objet retourné.
Callbacks
Le générateur doit produire des signatures de callbacks typées et documenter leur durée de vie.
Le binding est responsable de retenir les delegates ou callbacks aussi longtemps que le Runtime peut les invoquer. Le code applicatif ne doit pas manipuler directement des pointeurs de fonction lorsqu'un wrapper public existe.
Contrats async et cancellation
Le manifest doit exprimer explicitement les capacités :
- synchrone ;
- asynchrone ;
- attente annulable côté client ;
- transport annulable ;
- annulation coopérative Runtime ;
- annulation de job durable.
Cela évite d'inférer une sémantique à partir du nom de méthode ou de la topologie.
Tests générés
La génération d'un binding doit être accompagnée de tests de contrat. Au minimum :
- scalaires ;
- chaînes ;
- nullabilité ;
- enums ;
- tableaux ;
- objets ;
- ownership ;
- callbacks ;
- erreurs ;
- local/RPC lorsque la capacité est disponible.
La documentation elle-même doit pouvoir vérifier les symboles publics utilisés dans ses exemples contre le manifest correspondant à la release.
Règle de documentation
Les exemples de ce site utilisent uniquement les noms publics du binding. Les identifiants privés ou les conventions du langage d'implémentation du moteur ne font pas partie du vocabulaire développeur.
Voir aussi :