# ObjVLisp. > Cette note est liée à la compréhension et implémentation d'un **noyau minimal réfléchissant basé sur les classes** selon le programme guidé présenté au chapitre 2 du livre **“A simple reflective Object Kernel”**. ## Introduction JObjVlisp ~ Définition ObjVlisp a été publié pour la première fois en 1986, alors que les fondements de la programmation orientée objet émergeaient encore. ObjVlisp a des métaclasses explicites et prend en charge la réutilisation des métaclasses. Il a été inspiré du noyau de Smalltalk-78. Le noyau IBM SOM-DSOM est similaire à ObjVLisp lorsqu'il est implémenté en C ++. ObjVlisp est un sous-ensemble du noyau réfléchissant de CLOS puisque CLOS réifie les variables d'instance, les fonctions génériques et la combinaison de méthodes. Par rapport à ObjVlisp, Smalltalk ou Pharo ont des métaclasses implicites et aucune réutilisation de métaclasse sauf par héritage de base mais il est plus stable. ### Les six postulats d'ObjVLisp Le noyau ObjVlisp original est défini par six postulats. Certains d'entre eux semblent un peu datés par rapport aux normes d'aujourd'hui et le 6ème postulat est tout simplement faux comme nous l'expliquerons plus tard (une solution est simple à concevoir et à mettre en œuvre). Ici, nous les rapportons comme indiqué dans l'article pour des raisons de perspective historique. - Un objet représente une connaissance et un ensemble de capacités. Le seul protocole pour activer un objet est le passage de message: un message spécifie la procédure à appliquer (indiquée par son nom, le sélecteur) et ses arguments. - Chaque objet appartient à une classe qui spécifie ses données (attributs appelés champs) et son comportement (procédures appelées méthodes). Les objets seront générés dynamiquement à partir de ce modèle, ils sont appelés instances de la classe. Après Platon, toutes les instances d'une classe ont la même structure et la même forme, mais diffèrent par les valeurs de leurs variables d'instance communes. - Une classe est également un objet, instancié par une autre classe, appelée sa métaclasse. Par conséquent (P3), à chaque classe est associée une métaclasse qui décrit son comportement en tant qu'objet. La métaclasse primitive initiale est la classe Class, construite comme sa propre instance. - Une classe peut être définie comme une sous-classe d'une (ou plusieurs) autre (s) classe (s). Ce mécanisme de sous-classification permet le partage de variables d'instance et de méthodes, et s'appelle l'héritage. La classe Object représente le comportement le plus commun partagé par tous les objets. - Si les variables d'instance appartenant à un objet définissent un environnement local, il existe également des variables de classe définissant un environnement global partagé par toutes les instances d'une même classe. Ces variables de classe sont définies au niveau de la métaclasse selon l'équation suivante: variable de classe [un-objet] = variable d'instance [classe d'un-objet]. ## Construire un noyau basé sur des classes à réflexion minimale ### Hériter de la classe Array Dans ce guide il nous ai expliqué que nous ne voulons pas implémenter un scanner, un analyseur et un compilateur pour ObjVLisp mais nous concentrer sur l'essence du langage. C'est pourquoi nous avons choisi d'utiliser autant que possible le langage d'implémentation, ici Pharo. Comme Pharo ne prend pas en charge la définition de macro, nous utiliserons autant que possible les classes existantes pour éviter des problèmes syntaxiques supplémentaires. Ainci dans notre implémentation, chaque objet du monde ObjVLisp est une instance de la classe Obj. La classe Objest une sous-classe de Array. ```smalltalk #(#ObjPoint 10 15) ``` > Une instance objInstance de la classe ObjPointqui est également une instance de tableau de la classe Pharo ObjClass ### Structure et primitives Le premier problème est de savoir comment représenter des objets. Il faut s'entendre sur une représentation initiale. Dans cette implémentation, nous avons choisi de représenter les objets sous forme de tableaux (instances d' Objune sous-classe de Array). Dans ce qui suit, nous utilisons les termes tableau pour parler des instances de la classe Obj. #### Vérification de l'existance de la classe Obj et qu'elle hérite de Array. ```smalltalk Obj superclass. ``` ### Structure d'une classe #### ObjClassId ```smalltalk objClassId ^self at: self offsetForClass. ``` Pour connaitre l'id d'une classe il faut retourner le contenu qui ce trouve à l'indece OffsetForClass. ```smalltalk objClassId: anObjClassId self at: self offsetForClass put: anObjClassId. ``` Cette méthode permet de modifier l'id de la classe par la valeur passée en paramétre. #### ObjName ```smalltalk objName ^self at: self offsetForName ``` Pour connaitre le nom d'une classe il faut retourner le contenu qui ce trouve à l'indece offsetForName.Cela est rondu possible par le lien d'héritage entre Obj et Array (ici OBJ est une classe d'un array) ```smalltalk objName : aName self at: self offsetForName put: aName ``` La modification d'un nom d'une classe se réalise en modifiant le contenu se trouvant à l'inde offsetForName. #### ObjSuperClassId ```smalltalk objSuperClassId ^self at: self offsetForSuperclass ``` Le contenu à l'indice offsetForSuperclass contient l'id de la superclass. On renvoie donc le contenu de la case d'indice offsetForSuperclass. ```smalltalk objSuperclassId: anObjClassId self at: self offsetForSuperclass put: anObjClassId ``` La modification d'un nom d'une classe se réalise en modifiant le contenu se trouvant à l'inde offsetForSuperclass. #### ObjIVs ```smalltalk objIVs self at: self offsetForIVs ``` Définition d'une liste de noms des variables d'instances d'une ObjClass. ```smalltalk objIVs: anOrderedCollection ^self at: self offsetForIVs put: anOrderedCollection ``` #### ObjKeywords ```smalltalk objKeywords ^self at: self offsetForKeywords. ``` Le contenu à l'indice offsetForKeywords contient la liste des mots clés du receveur. On renvoie donc le contenu de la case d'indice offsetForKeywords avec la méthode at de la classe Array. ```smalltalk objKeywords: anOrderedCollection self at: self offsetForKeywords put: anOrderedCollection. ``` Cette méthode permet de modifier la collection des mots clés du receveur par la nouvelle collection passée en paramétre. #### ObjMethodDict ```smalltalk self at: self offsetForMethodDict ``` Le contenu à l'indice offsetForMethodDict contient la liste des mots clés du receveur. On renvoie donc le contenu de la case d'indice offsetForMethodDict avec la méthode at de la classe Array. ```smalltalk self at: self offsetForMethodDict put: aDictionary ``` La modification d'un nom d'une classe se réalise en modifiant le contenu se trouvant à l'inde offsetForMethodDict. ### ObjClass Cette méthode renvoie l'objet ObjClass de l'objet Obj, le receveur doit être un ObjObject. Diffère de la méthode classId qui elle permet d'accéder a la représentation interne de la classe. ```smalltalk ObjClass ^Obj giveClassNamed: self objClassId ``` | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| ### offsetFromClassOfInstanceVariable: aSymbol Cette méthode permet de retourner l'offset de la variable d'instance représentée par le symbole donné en paramètre. Il renvoie 0 si la variable n'est pas définie. ```smalltalk offsetFromClassOfInstanceVariable: aSymbol ^ self objIVs indexOf: aSymbol ``` La méthode de test testIVOffsetde de la classe s'est bien exécutée. | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| |ObjTest|:+1:| ### offsetFromObjectOfInstanceVariable: aSymbol Cette méthode permet de retourner l'offset de la variable d'instance nommmée aSymbol dans l'objet anObjObject. Si aSymbol n'est pas une variable d'instance alors on lève une erreur ```smalltalk offsetFromObjectOfInstanceVariable: aSymbol | aClass | aClass := self objClass. (aClass objIVs includes: aSymbol) ifFalse: [self error: 'The class ' , aClass objName asString , ' does not define the instance variable ' , aSymbol asString]. ^aClass offsetFromClassOfInstanceVariable: aSymbol. ``` ### valueOfInstanceVariable: aSymbol Cette méthode permet de renvoyer la valeur de la variable d'instance aSymbol de l'objet receveur en accédant à son indice (offset) grâce a la méthode précédemment définie. ```smalltalk valueOfInstanceVariable: aSymbol ^self at: (self offsetFromObjectOfInstanceVariable: aSymbol) ``` ### testIVOffsetAndValue | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| |ObjTest|:+1:| |testIVOffsetAndValue|:+1:| ### allocateAnInstance Cette méthode permet de retourner une nouvelle instance crée de "self", ObjClass. Dans cette implémentation, l'identificateur de object class représente le nom de la classe. ```smalltalk allocateAnInstance |a| a := Obj new: self numberOfIVs. a objClassId: self objName. ^a ``` ## Your Job 7 | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| |ObjTest|:+1:| |testIVOffsetAndValue|:+1:| |testKeywords|:+1:| ## La primitive computeNewIVFrom: superIVOrdCol with: localIVOrdCol. La méthode computeNewIVFrom prend deux collections de symboles et retourne une concaténation de ces deux collections en préservant l'ordre des éléments et en retirant les doublons. Ceci est la mise en oeuvre du paragraphe "Static inheritance of instance variables" ou il est dit qu'on à fait le choix d'implémentation de ne pas considérer les variables d'instances du même nom dans la chaîne d'héritage et de faire comme si il s'agissait d'une seule et même variable. ###### Exemple: ```smalltalk self assert: ((Obj new computeNewIVFrom: #(#n #a #s #s #e #r) asOrderedCollection with: #( #z #b #t) asOrderedCollection) = #( #n #a #s #s #e #r #z #b #t) asOrderedCollection). ``` ## Your Job 10 ```smalltalk bodyOfMethod: aSelector ^self objMethodDict at: aSelector ifAbsent: [nil] ``` | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| |ObjTest|:+1:| |testIVOffsetAndValue|:+1:| |testKeywords|:+1:| |testMethodManagement|:-1:| ## Methode lookup Implémentez la primitive lookup: selector qui a envoyé à une objClass avec un sélecteur de méthode, un symbole et le récepteur initial du message, retourne le corps de méthode de la méthode associée au sélecteur dans objClass ou ses superclasses. De plus, si la méthode n'est pas trouvée, nil est retourné. ```smalltalk lookup: selector | method Super| method:=self bodyOfMethod:selector. method isNil ifFalse:[ ^method. "method foundr" ]. "else" "we search in the superclass." ((self objSuperclassId) isNil) ifTrue:[ ^nil. "method not found" ]. Super:=self objSuperclass. Super==self ifTrue:[ ^nil ]. ^Super lookup:selector. ``` Dans cette méthode, on commence par regarder si la méthode est contenue dans la classe qui a reçu le message. Elle est contenue dans la classe alors nous avons terminé et on la renvoie. Si on ne réussit pas à la trouver dans cette classe alors on va regarder dans la classe sa super classe. Si la classe ne possède pas de super classe alors cela signifie que nous sommes arrivé au sommet de son arbre d'héritage et que nous l'avons pas trouvé. Si la classe possède une classe mère alors on applique le même processus de manière récessif pour la classe mère. ### super: selector withArguments: arguments from: aSuperclass ``` smalltalk super: selector withArguments: arguments from: aSuperclass ^(self objClass) objSuperclass basicSend: selector withArguments: arguments from: aSuperclass. ``` Ici on utilise la primitive basicSend:withArguments:from:. ### La primitive manualObjClassStructure Implémentation de la primitive manualObjClassStructurequi renvoie un objObject qui représente la classe ObjClass. ``` smalltalk manualObjClassStructure | class | class := Obj new: 6. class objClassId: #ObjClass. class objName: #ObjClass. class objIVs: #(#class #name #superclass #iv #keywords #methodDict). class objKeywords: #(#name: #superclass: #iv: #keywords: #methodDict:). class objSuperclassId: #ObjObject. class objMethodDict: (IdentityDictionary new: 3). ^class ``` | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| |ObjTest|:+1:| |testIVOffsetAndValue|:+1:| |testKeywords|:+1:| |testMethodManagement|:-1:| |testManuallyCreateObjClassStructure|:+1:| ### ObjObjectStructure #### Méthode objObjectStructure ```smalltalk objObjectStructure ^(self giveClassNamed: #ObjClass) send: #new withArguments: #(#(#name: #ObjObject #iv: #(#class))). ``` #### Méthode giveClassNamed: aSymbol ``` giveClassNamed: aSymbol "Return the class defined in the class repository with the name aSymbol" ^self giveClassNamed: aSymbol ifAbsent: [self error: ('The class ' , aSymbol printString , ' is not defined')] ``` #### Méthode giveClassNamed: aSymbol ifAbsent: aBlock ``` giveClassNamed: aSymbol ifAbsent: aBlock ^definedObjClasses at: aSymbol ifAbsent: aBlock ``` ### createObjClass ```smalltalk createObjClass | tmp1 | tmp1 := self objClassStructure. self defineAllocateMethodIn: tmp1; defineNewMethodIn: tmp1; defineInitializeMethodIn: tmp1. tmp1 addUnaryMethod: #isMetaclass withBody: 'objself objIVs includes: #superclass'; addUnaryMethod: #isClass withBody: 'objself objClass send: #isMetaclass withArguments:#()'. ^ tmp1! ! ``` | Test | Résultat| |:------ |:----------- | |testClassAccess | :+1:| |ObjTest|:+1:| |testIVOffsetAndValue|:+1:| |testKeywords|:+1:| |testMethodManagement|:-1:| |testManuallyCreateObjClassStructure|:+1:| |testCreateObjClassMessage|:+1:| # Contrubuteurs![](https://i.imgur.com/h2EiOtp.png) - [Abdellah Choukri](https://github.com/ChoukriAbdellah)