Ce didacticiel est destiné aux utilisateurs maîtrisant la programmation en C ou C++, ainsi que l'architecture de la Simple Virtual Machine.
Dans ce didacticiel, vous allez apprendre à introspecter et configurer les extensions depuis les extensions.
Le temps de lecture de ce didacticiel est estimé à 25 minutes.
Pour commencer, créez le canevas de l'extension dans le fichier extensions.svm_plugin en utilisant ce code :
PLUGIN plugin
DEFINE
La première manière d'introspecter les extensions est de demander la représentation textuelle des objets des extensions présents sur la machine virtuelle.
Modifiez le code de l'extension :
PLUGIN plugin
DEFINE
INSTRUCTION plugin.print -> STR
%{
SVM_String s = ::svm_plugin_print(svm);
return NEW_VALUE(string,s);
%}
Générez et compilez cette extenion, puis créez une petite application de test pour appeller cette instruction et en afficher le résultat dans le terminal.
Il est également possible de déterminer la présence d'objets de manière plus fine.
Commençons par les objets dont le seul nom suffit pour tester s'ils sont accessibles dans la machine virtuelle.
Modifiez le code de l'extension :
PLUGIN plugin
DEFINE
INSTRUCTION plugin.type PEP -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Boolean b = ::svm_plugin_has_type(svm,n);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION plugin.interruption PEP -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Boolean b = ::svm_plugin_has_interruption(svm,n);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION plugin.sequencer PEP -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Boolean b = ::svm_plugin_has_sequencer(svm,n);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION plugin.scheduler PEP -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Boolean b = ::svm_plugin_has_scheduler(svm,n);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION plugin.structure PEP -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Boolean b = ::svm_plugin_has_structure(svm,n);
return NEW_VALUE(boolean,b);
%}
Générez et compilez cette extenion, puis créez une petite application de test pour appeller ces instructions.
Ces fonctions d'interface programmatique sont très utiles pour tester une dépendance vers une autre extension de manière plus souple qu'avec la section USE
des extensions : en effet, ici, la machine peut quand même démarrer et la dépendance est testée plus tard dans l'exécution de l'application. De même, cela permet de tester des dépendances dynamiquement, comme présenté dans l'exemple ci-dessus.
Vous pouvez remarquer qu'il manque deux objets dans les requêtes simples : les instructions et les fonctions. En réalité, savoir qu'une instruction ou une fonction existe n'est pas utile en soit. Il est bien plus intéressant de vérifier si un appel à une instruction ou à une fonction est possible.
Modifiez le code de l'extension :
PLUGIN plugin
DEFINE
INSTRUCTION plugin.instruction PEP VALUE * -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Parameter r = ::svm_parameter_value_new(svm,::svm_value_boolean_new(svm,TRUE));
SVM_Boolean b = ::svm_plugin_has_instruction(svm,n,argc-1,argv+1,r);
return NEW_VALUE(boolean,b);
%}
INSTRUCTION plugin.function PEP -> BLN
%{
SVM_Value_PluginEntryPoint n = ::svm_parameter_value_get(svm,argv[0]);
SVM_Parameter *p = ::svm_parameter_array_new(svm,3);
p[0] = ::svm_parameter_value_new(svm,::svm_value_integer_new_null(svm));
p[1] = ::svm_parameter_marker_new__raw(svm,"<");
p[2] = ::svm_parameter_value_new(svm,::svm_value_string_new_null(svm));
SVM_Parameter r = ::svm_parameter_value_new(svm,::svm_value_boolean_new(svm,TRUE));
SVM_Boolean b = ::svm_plugin_has_function(svm,n,3,p,r);
return NEW_VALUE(boolean,b);
%}
Générez et compilez cette extenion, puis créez une petite application de test pour appeller la première instruction avec ce code :
#!/usr/bin/env svm
LOG
PLUGIN "svmcom.so"
LOCAL PLUGIN "svmpluginplugin/libsvmplugin.so"
PROCESS "plugin"
CODE "main" INLINE
:memory BLN*5/b
:plugin.instruction plugin.instruction p.e -> (b/0)
:plugin.instruction plugin.void 3 -> (b/1)
:plugin.instruction plugin.instruction "a" -> (b/2)
:plugin.instruction plugin.instruction p.e 1 2 3 -> (b/3)
:plugin.instruction com.message 1 2 3 -> (b/4)
:com.message @(b/0) " " @(b/1) " " @(b/2) " " @(b/3) " " @(b/4)
END
END
Vous pouvez lancer cette application :
./plugin.svm
TRUE FALSE FALSE TRUE FALSE
Interprétons alors le résultat :
plugin.instruction
accepte un point d'entrée d'extension et retourne un booléen,plugin.void
n'existe pas,plugin.instruction
n'accepte pas une chaine de caractères comme paramètre,plugin.instruction
accepte un point d'entrée d'extension suivi d'un nombre arbitraire de valeurs, et retourne un booléen,com.message
accepte n'importe quel nombre de valeurs, mais ne retourne pas un booléen.La fonction svm_plugin_has_function
réalise le même test sur les fonctions.
svm_plugin_has_type
, svm_plugin_has_interruption
, svm_plugin_has_sequencer
, svm_plugin_has_scheduler
et svm_plugin_has_structure
permettent de détecter la présence d'un objet d'extension de manière plus souple que la directive USE
des définitions d'extension.svm_plugin_has_instruction
et svm_plugin_has_function
permettent de détecter si l'appel à une instruction ou à une fonction peut être réalisé sans que l'appel lui-même soit erroné. Ces deux tests comprennent la vérification des types de paramètres et de la valeur de retour des instructions et des fonctions.En plus des objets destinés à l'application et à la machine virtuelle, les extensions permettent de définir des objets servant à la configuration des extensions depuis l'application. Ces objets sont accessibles depuis toutes les fonctions de l'extension, y compris ses fonctions d'initialisation, de démarrage et de finalisation.
Modifiez le code de l'extension :
PLUGIN plugin
DEFINE
OPTION plugin.flag -f BLN
OPTION plugin.integer -i INT
OPTION plugin.string -s STR
INSTRUCTION plugin.option PEP -> VALUE ?
%{
SVM_Value_PluginEntryPoint o = ::svm_parameter_value_get(svm,argv[0]);
if(not ::svm_plugin_has_option(svm,o))
{
return ::svm_value_interruption_new_internal__raw(svm,FAILURE,"Unknown option.",SOFTWARE);
}
return ::svm_plugin_get_option(svm,o);
%}
Avant de générer l'extension, constatez que :
FALSE
si l'option n'est pas présente, et TRUE
si elle l'est,svm_plugin_has_option
permet de détecter la présence d'un objet option, comme les autres objets d'extension. Notez que seule la définition de l'option est vérifiée, et pas sa présence sur la ligne de commande de l'extension.svm_plugin_get_option
permet de récupérer la valeur associée à une option.Générez et compilez l'extension. Puis, créez une petite application avec ce code :
#!/usr/bin/env svm
LOG
PLUGIN "svmrun.so"
LOCAL PLUGIN "svmpluginplugin/libsvmplugin.so"
PROCESS "plugin"
CODE "main" INLINE
:memory AUTO*4/o
:plugin.option plugin.flag -> (o/0)
:plugin.option plugin.integer -> (o/1)
:plugin.option plugin.string -> (o/2)
:plugin.option plugin.unknown -> (o/3)
:run.coredump STDOUT
END
END
Lancez cette application :
./plugin.svm
Kernel:
State: R, transmit_interruption
Processor:
State:
Next instruction: <main:1/6> (Current one: <main:1/5>)
Current memory : &0*0
Allocated memory: &0*4
Aliases : o
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Waiting interruptions:
Code at <main:1/5>:
:memory AUTO*4/o # <0> @(main, line 1)
:plugin.option plugin.flag -> (o/0) # <1> @(main, line 2)
:plugin.option plugin.integer -> (o/1) # <2> @(main, line 3)
:plugin.option plugin.string -> (o/2) # <3> @(main, line 4)
:plugin.option plugin.unknown -> (o/3) # <4> @(main, line 5)
====== HERE ======
:run.coredump STDOUT # <5> @(main, line 6) SYSTEM
==================
Labels:
Symbols:
Memory:
&0: BLN FALSE
&1: INT
&2: STR
&3: IRQ FAILURE
Aliases:
o -> &0*4
Free space:
Modifiez le code de l'application pour ajouter les options d'extension :
#!/usr/bin/env svm
LOG
PLUGIN "svmrun.so"
LOCAL PLUGIN "svmpluginplugin/libsvmplugin.so" -f -i 17 -s "value"
PROCESS "plugin"
CODE "main" INLINE
:memory AUTO*4/o
:plugin.option plugin.flag -> (o/0)
:plugin.option plugin.integer -> (o/1)
:plugin.option plugin.string -> (o/2)
:plugin.option plugin.unknown -> (o/3)
:run.coredump STDOUT
END
END
Puis relancez l'application :
./plugin.svm
Kernel:
State: R, transmit_interruption
Processor:
State:
Next instruction: <main:1/6> (Current one: <main:1/5>)
Current memory : &0*0
Allocated memory: &0*4
Aliases : o
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Waiting interruptions:
Code at <main:1/5>:
:memory AUTO*4/o # <0> @(main, line 1)
:plugin.option plugin.flag -> (o/0) # <1> @(main, line 2)
:plugin.option plugin.integer -> (o/1) # <2> @(main, line 3)
:plugin.option plugin.string -> (o/2) # <3> @(main, line 4)
:plugin.option plugin.unknown -> (o/3) # <4> @(main, line 5)
====== HERE ======
:run.coredump STDOUT # <5> @(main, line 6) SYSTEM
==================
Labels:
Symbols:
Memory:
&0: BLN TRUE
&1: INT 17
&2: STR "value"
&3: IRQ FAILURE
Aliases:
o -> &0*4
Free space:
Vous pouvez noter également que les valeurs sont visibles depuis le débugueur :
En plus des options, les extensions peuvent définir des arguments obligatoires, ainsi que des arguments optionnels, présents en extra sur la ligne de commande de l'extension.
Modifiez le code de l'extension :
PLUGIN plugin
DEFINE
OPTION plugin.flag -f BLN
OPTION plugin.integer -i INT
OPTION plugin.string -s STR
INSTRUCTION plugin.option PEP -> VALUE ?
%{
SVM_Value_PluginEntryPoint o = ::svm_parameter_value_get(svm,argv[0]);
if(not ::svm_plugin_has_option(svm,o))
{
return ::svm_value_interruption_new_internal__raw(svm,FAILURE,"Unknown option.",SOFTWARE);
}
return ::svm_plugin_get_option(svm,o);
%}
ARGUMENT plugin.integer INT
ARGUMENT plugin.string STR
ARGUMENTS plugin.extra
INSTRUCTION plugin.argument PEP -> VALUE
%{
SVM_Value_PluginEntryPoint o = ::svm_parameter_value_get(svm,argv[0]);
if(not ::svm_plugin_has_argument(svm,o))
{
return ::svm_value_interruption_new_internal__raw(svm,FAILURE,"Unknown argument.",SOFTWARE);
}
return ::svm_plugin_get_argument(svm,o);
%}
INSTRUCTION plugin.arguments PEP -> [ PTR IRQ ]
%{
SVM_Value_PluginEntryPoint o = ::svm_parameter_value_get(svm,argv[0]);
if(not ::svm_plugin_has_arguments(svm,o))
{
return ::svm_value_interruption_new_internal__raw(svm,FAILURE,"Unknown extra arguments.",SOFTWARE);
}
SVM_Value *t = ::svm_plugin_get_arguments(svm,o);
SVM_Size s=0;
for(SVM_Value *it=t ; *it ; ++it) { ++s; }
SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
::svm_memory_zone_append_internal__raw(svm,z,AUTOMATIC,s);
SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
::svm_memory_write_pointer(svm,CURRENT(kernel),p,t);
return p;
%}
De la même manière que pour les options, l'extension définit ici deux arguments obligatoire avec leur type associé, et les arguments en extra qui eux n'ont pas de type associé.
Générez et compilez l'extension. Puis, modifiez le code de l'application :
#!/usr/bin/env svm
LOG
PLUGIN "svmrun.so"
LOCAL PLUGIN "svmpluginplugin/libsvmplugin.so" -f -i 17 -s "value" -- 3 "other" 1 2 3 "four" 5
PROCESS "plugin"
CODE "main" INLINE
:memory AUTO*4/o, AUTO*5/a
:plugin.option plugin.flag -> (o/0)
:plugin.option plugin.integer -> (o/1)
:plugin.option plugin.string -> (o/2)
:plugin.option plugin.unknown -> (o/3)
:plugin.argument plugin.integer -> (a/0)
:plugin.argument plugin.string -> (a/1)
:plugin.argument plugin.unknown -> (a/2)
:plugin.arguments plugin.extra -> (a/3)
:plugin.arguments plugin.unknown -> (a/4)
:run.coredump STDOUT
END
END
Cette fois, l'exécution donne :
./plugin.svm
Kernel:
State: R, transmit_interruption
Processor:
State:
Next instruction: <main:1/11> (Current one: <main:1/10>)
Current memory : &0*0
Allocated memory: &0*14
Aliases : a o
Flags :
Cascaded flags :
Local interruption handlers:
Cascaded local interruption handlers:
Saved states:
Global interruption handlers:
Waiting interruptions:
Code at <main:1/10>:
:memory AUTO*4/o , AUTO*5/a # <0> @(main, line 1)
:plugin.option plugin.flag -> (o/0) # <1> @(main, line 2)
:plugin.option plugin.integer -> (o/1) # <2> @(main, line 3)
:plugin.option plugin.string -> (o/2) # <3> @(main, line 4)
:plugin.option plugin.unknown -> (o/3) # <4> @(main, line 5)
:plugin.argument plugin.integer -> (a/0) # <5> @(main, line 6)
:plugin.argument plugin.string -> (a/1) # <6> @(main, line 7)
:plugin.argument plugin.unknown -> (a/2) # <7> @(main, line 8)
:plugin.arguments plugin.extra -> (a/3) # <8> @(main, line 9)
:plugin.arguments plugin.unknown -> (a/4) # <9> @(main, line 10)
====== HERE ======
:run.coredump STDOUT # <10> @(main, line 11) SYSTEM
==================
Labels:
Symbols:
Memory:
&0: BLN TRUE
&1: INT 17
&2: STR "value"
&3: IRQ FAILURE
&4: INT 3
&5: STR "other"
&6: IRQ FAILURE
&7: PTR &9*5
&8: IRQ FAILURE
&9: INT 1
&10: INT 2
&11: INT 3
&12: STR "four"
&13: INT 5
Aliases:
a -> &4*5
o -> &0*4
Free space:
Notez que si moins de deux arguments sont présents sur la ligne de commande de l'extension, la machine refuse de démarrer.
Ici, vous pouvez retrouver la valeur des deux premiers arguments aux adresses &4
et &4
. Les valeurs des arguments en extra sont quant à eux écrits en mémoire et accessible via le pointeur &9*5
.
Tout comme les options, les valeurs d'arguments sont visibles dans le débugueur avec la position de l'argument sur la ligne de commande :
svm_plugin_has_option
, svm_plugin_has_argument
et svm_plugin_has_arguments
permettent de détecter l'existence des définitions d'options et d'arguments. Cette détection permet une gestion des dépendances plus souple que celle, toujours possible avec les options et arguments, de la directive USE
.svm_plugin_get_option
, svm_plugin_get_argument
et svm_plugin_get_arguments
permettent de récupérer les valeurs associées aux options et arguments. Ces valeurs proviennent directement du fichier d'application, depuis la ligne d'invocation de l'extension.Vous venez de voir comment introspecter et configurer les extensions depuis une extension.
L'introspection d'extension permet de manière générale d'assouplir la gestion des dépendances entre extensions, ainsi que de réaliser des dépendances dynamiques.
L'introspection d'extension est souvent utile lorsqu'une extension réalise un polymorphisme dynamique inter-extension, car il permet de réagir à l'absence d'une fonction de manière moins brutale que ce qui a pu être décrit dans un didacticiel précédent.
La possibilité de rendre les extensions configurables au travers d'options et d'arguments permet aux applications de les utiliser avec une plus grande souplesse. En particulier, une option pour empêcher une extension de créer des processus au démarrage peut être souhaitable, ainsi que de pouvoir changer certaines valeurs utilisées par les extensions dans leur fonctionnement pour éviter les collisions de comportement entre extensions.