Programmation réflexive
En informatique , la programmation réflexive ou réflexion est la capacité d’un processus à examiner, introspecter et modifier sa propre structure et son comportement. [1]
Contexte historique
Les premiers ordinateurs ont été programmés dans leurs langages d’assemblage natifs , qui étaient intrinsèquement réfléchissants [ citation nécessaire ] , car ces architectures originales pouvaient être programmées en définissant des instructions en tant que données et en utilisant un code auto-modifiable . Au fur et à mesure que la majeure partie de la programmation s’est déplacée vers des Langages compilés de niveau supérieur tels que Algol , Cobol , Fortran , Pascal et C , cette capacité de réflexion a largement disparu jusqu’à ce que de nouveaux Langages de programmation avec réflexion intégrée dans leurs systèmes de type apparaissent. [ citation nécessaire ]
La thèse de doctorat de Brian Cantwell Smith en 1982 a introduit la notion de réflexion computationnelle dans les Langages de programmation procéduraux et la notion d’ interpréteur méta-circulaire en tant que composant de 3-Lisp . [2] [3]
Les usages
Reflection aide les programmeurs à créer des bibliothèques de logiciels génériques pour afficher les données, traiter différents formats de données, effectuer la sérialisation ou la désérialisation des données pour la communication, ou regrouper et dégrouper les données pour les conteneurs ou les rafales de communication.
L’utilisation efficace de la réflexion nécessite presque toujours un plan : un cadre de conception, une description d’encodage, une bibliothèque d’objets, une carte d’une base de données ou des relations d’entité.
La réflexion rend un langage plus adapté au code orienté réseau. Par exemple, il aide les langages tels que Java à bien fonctionner dans les réseaux en activant les bibliothèques pour la sérialisation, le regroupement et la variation des formats de données. Les langages sans réflexion tels que C doivent utiliser des compilateurs auxiliaires pour des tâches telles que la notation de syntaxe abstraite afin de produire du code pour la sérialisation et le regroupement.
La réflexion peut être utilisée pour observer et modifier l’exécution du programme au moment de l’ exécution . Un composant de programme orienté réflexion peut surveiller l’exécution d’une enceinte de code et peut se modifier selon un objectif souhaité de cette enceinte. Ceci est généralement accompli en affectant dynamiquement le code du programme au moment de l’exécution.
Dans les Langages de programmation orientés objet tels que Java , la réflexion permet l’ inspection des classes, des interfaces, des champs et des méthodes au moment de l’exécution sans connaître les noms des interfaces, des champs et des méthodes au moment de la compilation. Il permet également l’instanciation de nouveaux objets et l’invocation de méthodes.
La réflexion est souvent utilisée dans le cadre des tests de logiciels , par exemple pour la création/l’instanciation d’ objets fictifs à l’exécution .
La réflexion est également une stratégie clé pour la métaprogrammation .
Dans certains Langages de programmation orientés objet tels que C# et Java , la réflexion peut être utilisée pour contourner les règles d’ Accessibilité des membres . Pour les propriétés C#, cela peut être réalisé en écrivant directement sur le champ de sauvegarde (généralement invisible) d’une propriété non publique. Il est également possible de trouver des méthodes non publiques de classes et de types et de les invoquer manuellement. Cela fonctionne pour les fichiers internes au projet ainsi que pour les bibliothèques externes telles que les assemblages de .NET et les archives de Java.
Mise en œuvre
Apprendre encore plus Cette section ne cite aucune source . ( janvier 2008 )Veuillez aider à améliorer cette section en ajoutant des citations à des sources fiables . Le matériel non sourcé peut être contesté et supprimé . (Découvrez comment et quand supprimer ce modèle de message) |
Un langage prenant en charge la réflexion fournit un certain nombre de fonctionnalités disponibles au moment de l’exécution qui seraient autrement difficiles à réaliser dans un langage de niveau inférieur. Certaines de ces fonctionnalités sont les capacités de :
- Découvrez et modifiez les constructions de code source (telles que les blocs de code, les classes , les méthodes, les protocoles, etc.) en tant qu’objets de première classe au moment de l’ exécution .
- Convertit une chaîne correspondant au nom symbolique d’une classe ou d’une fonction en une référence ou un appel à cette classe ou fonction.
- Évalue une chaîne comme s’il s’agissait d’une instruction de code source au moment de l’exécution.
- Créez un nouvel interpréteur pour le bytecode du langage afin de donner une nouvelle signification ou un nouveau but à une construction de programmation.
Ces fonctionnalités peuvent être implémentées de différentes manières. Dans MOO , la réflexion fait naturellement partie du langage de programmation quotidien. Lorsque des verbes (méthodes) sont appelés, diverses variables telles que verb (le nom du verbe appelé) et this (l’objet sur lequel le verbe est appelé) sont renseignées pour donner le contexte de l’appel. La sécurité est généralement gérée en accédant par programmation à la pile des appelants : puisque les appelants () sont une liste des méthodes par lesquelles le verbe actuel a finalement été appelé, effectuer des tests sur les appelants () [0] (la commande invoquée par l’utilisateur d’origine) permet au verbe pour se protéger contre une utilisation non autorisée.
Les Langages compilés s’appuient sur leur système d’exécution pour fournir des informations sur le code source. Un exécutable Objective-C compilé , par exemple, enregistre les noms de toutes les méthodes dans un bloc de l’exécutable, fournissant une table pour les faire correspondre avec les méthodes sous-jacentes (ou les sélecteurs pour ces méthodes) compilées dans le programme. Dans un langage compilé qui prend en charge la création de fonctions à l’exécution, comme Common Lisp , l’environnement d’exécution doit inclure un compilateur ou un interpréteur.
La réflexion peut être implémentée pour les langages sans réflexion intégrée en utilisant un système de transformation de programme pour définir des modifications automatisées du code source.
Considérations de sécurité
La réflexion peut permettre à un utilisateur de créer des chemins de flux de contrôle inattendus via une application, contournant potentiellement les mesures de sécurité. Cela peut être exploité par des attaquants. [4] Les vulnérabilités historiques de Java causées par une réflexion non sécurisée ont permis au code extrait de machines distantes potentiellement non fiables de sortir du mécanisme de sécurité du bac à sable Java. Une étude à grande échelle de 120 vulnérabilités Java en 2013 a conclu que la réflexion non sécurisée est la vulnérabilité la plus courante en Java, mais pas la plus exploitée. [5]
Exemples
Les extraits de code suivants créent une instance foo de class Foo et appellent sa méthode PrintHello . Pour chaque langage de programmation , des séquences d’appels normales et basées sur la réflexion sont affichées.
C#
Voici un exemple en C# :
// Sans réflexion Foo foo = new Foo (); fou . ImprimerBonjour (); // Avec réflexion Object foo = Activator . CreateInstance ( “complete.classpath.and.Foo” ); Méthode MethodInfo = foo . GetType (). GetMethod ( “PrintHello” ); méthode . Invoquer ( foo , null );
Delphi / Pascal Objet
Cet exemple Delphi / Object Pascal suppose qu’une classe TFoo a été déclarée dans une unité appelée Unit1 :
utilise RTTI , Unit1 ; procédure SansRéflexion ; var Foo : TFoo ; commencer Foo := TFoo . Créer ; essayez Foo . Bonjour ; enfin Foo . Gratuit ; fin ; fin ; procédure AvecReflexion ; var RttiContext : TRttiContext ; RttiType : TRttiInstanceType ; Foo : TObject ; begin RttiType := RttiContext . FindType ( ‘Unit1.TFoo’ ) as TRttiInstanceType ; Foo := RttiType . GetMethod ( ‘Créer’ ) . Appelez ( RttiType . MetaclassType , []) . AsObject ; essayez RttiType . GetMethod ( ‘Bonjour’ ) . Invoquer ( Foo , []) ; enfin Foo . Gratuit ; fin ; fin ;
ce
Voici un exemple dans eC :
// Sans réflexion Foo foo { } ; fou . bonjour (); // Avec réflexion Class fooClass = eSystem_FindClass ( __thisModule , “Foo” ); Instance foo = eInstance_New ( fooClass ); Méthode m = eClass_FindMethod ( fooClass , “hello” , fooClass . module ); (( void ( * )())( void * ) m . fonction )( foo );
Aller
Voici un exemple en Go :
importer “refléter” // Sans réflexion f := Foo {} f . Bonjour () // Avec réflexion fT := reflect . TypeOf ( Foo {}) fV := refléter . Nouveau ( fT ) m := fV . MethodByName ( “Bonjour” ) si m . Est valide () { m . Appel ( néant ) }
Java
Voici un exemple en Java :
import java.lang.reflect.Method ; // Sans réflexion Foo foo = new Foo (); fou . bonjour (); // Avec réflexion, essayez { Object foo = Foo . classe . nouvelleInstance (); Méthode m = foo . getClass (). getDeclaredMethod ( “bonjour” , nouvelle classe <?>[ 0 ] ); m . invoquer ( foo ); } catch ( ReflectiveOperationException ignoré ) {}
Javascript
Voici un exemple en JavaScript :
// Sans réflexion const foo = new Foo () foo . bonjour () // Avec réflexion const foo = Reflect . construct ( Foo ) const bonjour = Refléter . get ( foo , ‘hello’ ) Reflect . appliquer ( bonjour , foo , []) // Avec eval eval ( ‘new Foo().hello()’ )
Julia
Voici un exemple en Julia (langage de programmation) :
julia> struct Point x :: Int y end # Inspection avec réflexion julia> fieldnames ( Point ) (:x, :y) julia> types de champs ( Point ) (Int64, Any) julia> p = Point ( 3 , 4 ) # Accès avec réflexion julia> getfield ( p , 😡 ) 3
Objectif c
Voici un exemple en Objective-C , impliquant que le framework OpenStep ou Foundation Kit est utilisé :
// Classe Foo. @interface Foo : NSObject – ( vide ) bonjour ; @finir // Envoi de “hello” à une instance de Foo sans réflexion. Foo * obj = [[ Foo alloc ] init ] ; [ obj bonjour ] ; // Envoi de “hello” à une instance de Foo avec réflexion. id obj = [[ NSClassFromString ( @”Foo” ) alloc ] init ] ; [ obj performSelector : @selector ( bonjour )];
perle
Voici un exemple en Perl :
# Sans réflexion my $foo = Foo -> new ; $foo -> bonjour ; # ou Foo -> nouveau -> bonjour ; # Avec réflexion my $class = “Foo” my $constructor = “new” ; ma $méthode = “bonjour” ; mon $f = $classe -> $constructeur ; $f -> $méthode ; # ou $classe -> $constructeur -> $méthode ; # avec eval eval “nouveau Foo->hello ;” ;
PHP
Voici un exemple en PHP :
// Sans réflexion $foo = new Foo (); $foo -> bonjour (); // Avec réflexion, en utilisant l’API Reflections $reflector = new ReflectionClass ( ‘Foo’ ); $foo = $reflector -> nouvelleInstance (); $bonjour = $réflecteur -> getMethod ( ‘bonjour’ ); $bonjour -> invoquer ( $foo );
Python
Voici un exemple en Python :
# Sans réflexion obj = Foo () obj . bonjour () # Avec la réflexion obj = globals ()[ “Foo” ]() getattr ( obj , “hello” )() # Avec eval eval ( “Foo().hello()” )
R
Voici un exemple en R :
# Sans réflexion, en supposant que foo() renvoie un objet de type S3 qui a la méthode “hello” obj <- foo () hello ( obj ) # Avec réflexion class_name <- “foo” generic_having_foo_method <- “hello” obj <- do.call ( class_name , list ()) do.call ( generic_having_foo_method , alist ( obj ))
Rubis
Voici un exemple en Ruby :
# Sans réflexion obj = Foo . nouvel objet . salut # Avec réflexion class_name = “Foo” method_name = :hello obj = Object . const_get ( nom_classe ) . nouvel objet . envoyer method_name # Avec eval eval “Foo.new.hello”
Xojo
Voici un exemple utilisant Xojo :
‘ Sans réflexion Dim fooInstance As New Foo fooInstance . ImprimerBonjour ‘ Avec réflexion Dim classInfo As Introspection . Typeinfo = GetTypeInfo ( Foo ) Dim constructeurs () As Introspection . ConstructorInfo = classInfo . GetConstructors Dim fooInstance As Foo = constructeurs ( 0 ). Appelez les méthodes Dim () As Introspection . MethodInfo = classInfo . GetMethods pour chaque m Comme introspection . MethodInfo Dans les méthodes If m . Nom = “PrintHello” Alors m . Invoke ( fooInstance ) End If Next
Voir également
- Liste des langages et plates-formes de programmation réflexifs
- Miroir (programmation)
- Paradigmes de programmation
- Auto-hébergement (compilateurs)
- Code auto-modifiable
- Introspection des types
- Type de
Références
Citations
- ^ Un tutoriel sur la réflexion comportementale et sa mise en œuvre par Jacques Malenfant et al. (PDF) , inconnu, archivé de l’original (PDF) le 21 août 2017 , récupéré le 23 juin 2019
- ^ Brian Cantwell Smith, Réflexion procédurale dans les Langages de programmation , Département de génie électrique et d’informatique, Massachusetts Institute of Technology, thèse de doctorat, 1982.
- ^ Brian C. Smith. Réflexion et sémantique dans un langage procédural Archivé le 13/12/2015 à la Wayback Machine . Rapport technique MIT-LCS-TR-272, Massachusetts Institute of Technology, Cambridge, Massachusetts, janvier 1982.
- ^ Barros, Paulo; Juste, René; Millstein, Suzanne; Vignes, Paul ; Dietl, Werner; d’Amorim, Marcelo; Ernst, Michael D. (août 2015). Analyse statique du flux de contrôle implicite : résolution de la réflexion Java et des intentions Android (PDF) (rapport). Université de Washington. UW-CSE-15-08-01 . Consulté le 7 octobre 2021 .
- ↑ Eauvidoum , Ieu ; bruit de disque (5 octobre 2021). “Vingt ans d’évasion du bac à sable Java” . Phrack . Vol. 10, non. 46 . Consulté le 7 octobre 2021 . {{cite magazine}}: Maint CS1 : utilise le paramètre auteurs ( lien )
Sources
- Jonathan M. Sobel et Daniel P. Friedman. Une introduction à la programmation orientée réflexion (1996), Université de l’Indiana .
- Technique anti-reflet utilisant C# et C++/CLI wrapper pour empêcher le voleur de code
Lectures complémentaires
- Ira R. Forman et Nate Forman, Java Reflection in Action (2005), ISBN 1-932394-18-4
- Ira R. Forman et Scott Danforth, Mettre les métaclasses au travail (1999), ISBN 0-201-43305-2
Liens externes
- Réflexion en programmation logique, fonctionnelle et orientée objet : une courte étude comparative
- Une introduction à la programmation orientée réflexion
- Les pages de Brian Foote sur la réflexion dans Smalltalk
- Tutoriel de l’API Java Reflection d’Oracle