Alors, comment puis-je écrire un processeur de symboles Kotlin (KSP) ?
Toute la logique de génération de code de notre processeur sera implémentée dans Visitor
Salle de classe.
Étape 1. Traitement des déclarations de classe
Notre processeur doit travailler avec des notes interfaces. Dans KSP, la déclaration d’interface est représentée par un KSClassDeclaration
maquette. Nous commençons donc le traitement en visitant les déclarations de classe.
Aller à visitClassDeclaration
méthode d’un Visitor
et ajoutez le code d’implémentation.
Généralement KSClassDeclaration
décrit toutes sortes de classes, telles que class
, data class
, interface
et d’autres. Nous devons donc nous assurer que la classe annotée est vraiment une interface vérifier le classKind
biens. Sinon, nous arrêterons le traitement.
Ensuite, nous devons obtenir @Function
annotation de la déclaration de classe. Dans Kotlin, les interfaces peuvent être annotées avec diverses annotations, comme nous le savons. Par conséquent, nous devons trouver le bon par son nom.
Ensuite, nous devons obtenir les informations des arguments d’annotation. Dans notre exemple, nous devons trouver le nom de la fonction générée en identifiant la valeur de la name
argument de l’annotation.
Après cela, nous pouvons faire la vraie génération.
Continuez à mettre en œuvre visitClassDeclaration
fonction avec le code ci-dessous.
Tout d’abord, nous devons identifier tous les Propriétés que possède l’interface annotée. Ce seront les arguments d’une fonction générée.
Ensuite, nous commençons à générer la signature de la fonction en déléguant la génération d’arguments à visitPropertyDeclaration
Occupation.
Enfin, lorsque la signature de fonction est générée, nous ajoutons un corps simple qui imprime Hello from <function_name>
un message.
Étape 2. Traitement des réclamations de propriété
voyons comment visitPropertyDeclaration
est implémenté.
Pour chaque propriété, nous devons simplement générer des arguments de fonction dans un format. argName: ArgType
.
Tandis que argName
la partie est simple, il suffit d’obtenir simpleName
propriété de la déclaration de propriété, le ArgType
partie nécessite la prise en compte de 2 étapes supplémentaires.
d’abord, nous devons non seulement générer le type d’argument, mais nous devons également nous assurer qu’il est importé au fichier. Cela signifie que nous devons avoir le nom complet du type. Ceci peut être fait de deux façons:
- Utilisation de la déclaration d’importation :
import com.package.ArgType
. - Utilisation du nom complet du type chaque fois que nous l’utilisons :
argName: com.package.ArgType
.
Nous utiliserons la deuxième option car elle est plus facile à mettre en œuvre. Nous n’aurons pas besoin d’aller et venir entre les déclarations d’importation et les endroits dans le code où le type est utilisé lors de la génération de code.
Alors, comment obtenons-nous le nom complet du type ?
si on utilise KSPropertyDeclaration.type
, on obtient l’objet de type KSTypeReference
. Cependant, pour plus d’informations sur le type, KSTypeReference
nous devrions être trié appel resolve
fonction qui retourne l’objet de type KSType
.
Ainsi, lorsque vous avez un objet de type KSType
, nous pouvons obtenir le nom complet en utilisant le code ci-dessous.
resolvedType.declaration.qualifiedName?.asString
Observation. O
resolve
Opération il est coûteux en calcul car le KSP devra aller chercher où le type est déclaré. Par conséquent, il doit être utilisé avec précaution.
En deuxième place, nous devons traiter les cas où le type a des paramètres génériques, tels que ArgType<String>
. Cela nous oblige à implémenter des fonctionnalités supplémentaires.
Étape 3. Traitement des arguments de type générique
Pour chaque type d’argument que nous résolvons dans visitPropertyDeclaration
nous devons résoudre séparément leurs arguments génériques qui sont déclarés entre crochets < >
.
AuVisitor
classe entre visitPropertyDeclaration
et visitTypeArgument
Les fonctions déclarent une fonction d’assistance appelée. visitTypeArguments
(forme pluriel) qui prend la liste d’arguments de type.
La fonction ci-dessus définira le formatage du bloc d’arguments de type générique et parcourra chaque appel visitTypeArgument
Occupation.
Commençons à mettre en œuvre le visitTypeArgument
Occupation.
Lorsque vous travaillez avec des génériques dans Kotlin, nous devrions également considérer quelque chose appelé variance. (voir la documentation officielle de Kotlin pour en savoir plus).
Il y a 4 types de variation que nous devrions considérer :
- Star –
Type<*>
(on vient de générer*
symbole au lieu du type d’argument) - covariant –
Type<out OtherType>
(Nous ajoutonsout
avant le vrai type) - contravariant –
Type<in OtherType>
(Nous ajoutonsin
avant le vrai type) - invariant –
Type<OtherType>
(ajoutez simplement votre propre type d’argument)
Après avoir traité la variation de type, nous devons identifier le type réel de l’argument générique.
Nous appliquons la même approche que nous avons utilisée pour définir le type de propriété. Tout d’abord, nous résolvons KSTypeReference
puis obtenez le nom complet de KSType
:
resolvedType?.declaration?.qualifiedName?.asString()
N’oubliez pas de rendre le type d’argument nullable en ajoutant ?
, si nécessaire.
Enfin, nous devons traiter les types d’arguments imbriqués tels que Type<OtherType<OtherType>>
.
C’est une tâche très simple, car nous avons déjà mis en œuvre tous les composants nécessaires pour cela. il suffit d’appeler visitTypeArguments
(forme pluriel) à l’intérieur visitTypeArgument
pour ses types d’arguments imbriqués et fera le travail de manière récursive.
C’est tout ce dont nous avons besoin pour implémenter notre processeur. Maintenant, nous pouvons l’exécuter et voir comment cela fonctionne.
Ouvert main-project/src/main/kotlin/com/morfly/Main.kt
fichier et remplacez son contenu par le code ci-dessous.
Nous y sommes presque…
Par défaut, votre IDE ne connaît rien du code généré avec KSP. Pour vous aider à reconnaître les fichiers générés, ouvrez main-project/build.gradle.kts
et ajoutez le code ci-dessous quelque part dans le fichier.
Lors de l’ajout de ce paramètre, nous marquons explicitement un répertoire avec le code généré en tant que répertoire source Kotlin.
Observation. N’oubliez pas synchroniser Changements de niveau.
Exécutez maintenant le main
rôle dans main-project/.../Main.kt
Archiver.
Pour vérifier que la génération de code a réussi, ouvrez
main-project/build/generated/ksp/.../GeneratedFunctions.kt
fichier et vérifiez son contenu.
Félicitations, nous venons d’implémenter la fonctionnalité principale de notre processeur de symboles Kotlin !
Commentaires
Laisser un commentaire