# Introduction

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 découvrir la notion de variable Simple Virtual Machine.

Le temps de lecture de ce didacticiel est estimé à 25 minutes.

# Mise en place

Pour commencer, créez le canevas de l'extension dans le fichier variables.svm_plugin en utilisant ce code :

    PLUGIN variables

    DEFINE

    INSTRUCTION variables.repeat INT STR -> STR
    %{
    %}

    INSTRUCTION variables.set VALUE
    %{
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    %}

    INSTRUCTION variables.print -> STR
    %{
    %}

    INSTRUCTION variables.reset
    %{
    %}

# Un premier constat

Modifiez le fichier d'extension pour ajouter l'implémentation de la première instruction :

    PLUGIN variables

    DEFINE

    INSTRUCTION variables.repeat INT STR -> STR
    %{
    	SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[0]);
    	long long int i = ::svm_value_integer_get(svm,vi);
    	SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_String s = ::svm_value_string_get(svm,vs);
    	std::string rs(s.string,s.size);
    	std::string r;
    	if(i<0)
    	{
    		::svm_processor_current_raise_error_internal__raw(svm,FAILURE,"Invalid repetition");
    	}
    	for(size_t ii=0 ; ii<i ; ++ii)
    	{
    		r += rs;
    	}
    	SVM_Value_String vsres = ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
    	return vsres;
    %}

    INSTRUCTION variables.set VALUE
    %{
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    %}

    INSTRUCTION variables.print -> STR
    %{
    %}

    INSTRUCTION variables.reset
    %{
    %}

Ici, l'implémentation est volontairement détaillée, en remplaçant les macros du générateur d'extension par leur contenu.

A y regarder de plus près, nous pouvons constater que :

- On retrouve les variables magiques du générateur.
- Certaines variables ne sont pas liées directement aux fonctions de l'interface programmatique.
- Les autres sont des types C issus de l'interface programmatique.

Dans le fichier d'interface programmatique, on trouve ces définitions :

- SVM_String est une structure C qui contient deux champs,
- SVM_Value_Integer et SVM_Value_String sont en revanche deux pointeurs génériques !

Ces pointeurs sont non seulement retournés par l'interface, mais aussi consommés en tant que paramètres de fonctions de l'interface. De plus, lorsque la fonction `svm_processor_current_raise_error_internal__raw` interrompt brutalement l'exécution de l'instruction, ces pointeurs ne semblent pas provoquer de fuite mémoire (ce qui peut être vérifié en exécutant la machine avec l'utilitaire valgrind).

Ces pointeurs sont en réalité liés aux valeurs qu'ils représentent, mais celles-ci sont conservées par la machine virtuelle dans l'environnement d'exécution de l'instruction. Ces pointeurs, jouant le rôle de clef pour retrouver les valeurs dans l'environnement, sont des variables Simple Virtual Machine.

- Les valeurs de la machine virtuelle ne sont pas directement accessibles depuis le code C/C++.
- Elles sont accessibles à travers des pointeurs servant de clef représentant les valeurs de la machine : ces pointeurs sont des variables Simple Virtual Machine.

# Portée des variables SVM

## Portée locale

Modifiez le fichier d'extension pour ajouter une variable Simple Virtual Machine en statique et l'implémentation des instructions set, get et print :

    PLUGIN variables

    code:
    %{
    	SVM_Value _value = nullptr;
    %}

    DEFINE

    INSTRUCTION variables.repeat INT STR -> STR
    %{
    	SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[0]);
    	long long int i = ::svm_value_integer_get(svm,vi);
    	SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_String s = ::svm_value_string_get(svm,vs);
    	std::string rs(s.string,s.size);
    	std::string r;
    	if(i<0)
    	{
    		::svm_processor_current_raise_error_internal__raw(svm,FAILURE,"Invalid repetition");
    	}
    	for(size_t ii=0 ; ii<i ; ++ii)
    	{
    		r += rs;
    	}
    	SVM_Value_String vsres = ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
    	return vsres;
    %}

    INSTRUCTION variables.set VALUE
    %{
    	_value = ::svm_parameter_value_get(svm,argv[0]);
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    	return _value;
    %}

    INSTRUCTION variables.print -> STR
    %{
    	SVM_String s = ::svm_value_print(svm,_value);
    	return NEW_VALUE(string,s);
    %}

    INSTRUCTION variables.reset
    %{
    %}

Puis générez l'extension et compilez la. Enfin, crééz un fichier exécutable variables.svm avec ce code :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginvariables/libsvmvariables.so"
    PROCESS "variables"
    	CODE "main" INLINE
    		:memory INT/i, STR/s
    		:variables.set 17
    		:variables.print -> &s
    		:com.message @&s
    	END
    END

Cette application utilise la valeur statique de l'extension pour réaliser une action. Lancez l'application :

    ./variables.svm
    ### Simple Virtual Machine 12345 : PROCESS variables | 2023-01-01 00:00:00 GMT #######################################################################
    Kernel interrupted: @(main, line 3) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Core dump:
    Kernel:
    State: I, transmit_interruption, interrupted @(main, line 3) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Processor:
     State:
       Next instruction: <main:1/3> (Current one: <main:1/2>)
       Current memory  : &0*0
       Allocated memory: &0*2
       Aliases         : i s
       Flags           :
       Cascaded flags  :
       Local interruption handlers:
       Cascaded local interruption handlers:
     Saved states:
     Global interruption handlers:

    Code at <main:1/2>:
         :memory INT/i , STR/s      # <0> @(main, line 1)
         :variables.set 17  # <1> @(main, line 2)
    ====== HERE ======
         :variables.print -> &s     # <2> @(main, line 3)
    ==================
         :com.message @&s   # <3> @(main, line 4)
     Labels:
     Symbols:

    Memory:
      &0: INT
      &1: STR
     Aliases:
      i -> &0*1
      s -> &1*1
     Free space:

    ### Simple Virtual Machine 12345 : SYSTEM | 2023-01-01 00:00:00 GMT ##################################################################################
    Process variables interrupted: @(main, line 3) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

Et là, une erreur pour le moins étrange apparaît ! Dans l'instruction print, la fonction de l'interface `svm_value_print` ne reconnaît plus la variable "_value".

Et pour cause, par défaut, une variable n'est valable que dans le bloc de code où elle est définie : la variable Simple Virtual Machine est ici locale (au bloc de code).

En particulier, cela signifie que la valeur associée peut être détruite à la sortie de l'instruction : il n'est donc pas nécessaire de supprimer les variables Simple Virtual Machine lorsque celles-ci ne sont utilisées que dans le bloc de code où elles sont définies, en C ou en C++.

## Portée globale

Modifiez à nouveau le code de l'extension :

    PLUGIN variables

    code:
    %{
    	SVM_Value _value = nullptr;
    %}

    DEFINE

    INSTRUCTION variables.repeat INT STR -> STR
    %{
    	SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[0]);
    	long long int i = ::svm_value_integer_get(svm,vi);
    	SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_String s = ::svm_value_string_get(svm,vs);
    	std::string rs(s.string,s.size);
    	std::string r;
    	if(i<0)
    	{
    		::svm_processor_current_raise_error_internal__raw(svm,FAILURE,"Invalid repetition");
    	}
    	for(size_t ii=0 ; ii<i ; ++ii)
    	{
    		r += rs;
    	}
    	SVM_Value_String vsres = ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
    	return vsres;
    %}

    INSTRUCTION variables.set VALUE
    %{
    	_value = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    	return _value;
    %}

    INSTRUCTION variables.print -> STR
    %{
    	SVM_String s = ::svm_value_print(svm,_value);
    	return NEW_VALUE(string,s);
    %}

    INSTRUCTION variables.reset
    %{
    	::svm_variable_scope_set_local(svm,_value);
    %}

Puis regénérez l'extension et recompilez la. Modifiez également le fichier variables.svm pour obtenir :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginvariables/libsvmvariables.so"
    PROCESS "variables"
    	CODE "main" INLINE
    		:memory INT/i, STR/s
    		:variables.set 17
    		:variables.print -> &s
    		:com.message "print: " @&s
    		:variables.get -> &i
    		:com.message "get: " @&i
    		:variables.reset
    		:variables.print -> &s
    		:com.message "print: " @&s
    	END
    END

Lancez maintenant l'application :

    ./variables.svm
    print: 17
    get: 17
    ### Simple Virtual Machine 1234 : PROCESS variables | 2023-01-01 00:00:00 GMT ########################################################################
    Kernel interrupted: @(main, line 8) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Core dump:
    Kernel:
    State: I, transmit_interruption, interrupted @(main, line 8) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Processor:
     State:
       Next instruction: <main:1/8> (Current one: <main:1/7>)
       Current memory  : &0*0
       Allocated memory: &0*2
       Aliases         : i s
       Flags           :
       Cascaded flags  :
       Local interruption handlers:
       Cascaded local interruption handlers:
     Saved states:
     Global interruption handlers:

    Code at <main:1/7>:
         :memory INT/i , STR/s      # <0> @(main, line 1)
         :variables.set 17  # <1> @(main, line 2)
         :variables.print -> &s     # <2> @(main, line 3)
         :com.message "print: " @&s # <3> @(main, line 4)
         :variables.get -> &i       # <4> @(main, line 5)
         :com.message "get: " @&i   # <5> @(main, line 6)
         :variables.reset   # <6> @(main, line 7)
    ====== HERE ======
         :variables.print -> &s     # <7> @(main, line 8)
    ==================
         :com.message "print: " @&s # <8> @(main, line 9)
     Labels:
     Symbols:

    Memory:
      &0: INT 17
      &1: STR "17"
     Aliases:
      i -> &0*1
      s -> &1*1
     Free space:

    ### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
    Process variables interrupted: @(main, line 8) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

Observez attentivement où est générée l'erreur cette fois : après l'appel à l'instruction reset. Cependant, les deux premières lignes du résultat montrent que la variable était maintenant connue des instructions get et print !

L'appel à la fonction `svm_variable_scope_set_global` a permis de changer la portée de la variable Simple Virtual Machine et la rendre visible aux autres blocs de code présents dans l'extension : la variable Simple Virtual Machine est devenue globale.

Avec l'appel à la fonction `svm_variable_scope_set_local`, la variable redevient locale au bloc de code où cette fonction de l'interface programmatique est appellée.

## Suppression immédiate

Dans certains cas, il peut être important de révoquer immédiatement une variable Simple Virtual Machine, sans attendre la fin du bloc de code.

Modifiez le code de l'extension :

    PLUGIN variables

    code:
    %{
    	SVM_Value _value = nullptr;
    %}

    DEFINE

    INSTRUCTION variables.repeat INT STR -> STR
    %{
    	SVM_Value_Integer vi = ::svm_parameter_value_get(svm,argv[0]);
    	long long int i = ::svm_value_integer_get(svm,vi);
    	SVM_Value_String vs = ::svm_parameter_value_get(svm,argv[1]);
    	SVM_String s = ::svm_value_string_get(svm,vs);
    	std::string rs(s.string,s.size);
    	std::string r;
    	if(i<0)
    	{
    		::svm_processor_current_raise_error_internal__raw(svm,FAILURE,"Invalid repetition");
    	}
    	for(size_t ii=0 ; ii<i ; ++ii)
    	{
    		r += rs;
    	}
    	SVM_Value_String vsres = ::svm_value_string_new__buffer(svm,r.c_str(),r.size());
    	return vsres;
    %}

    INSTRUCTION variables.set VALUE
    %{
    	_value = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    	return _value;
    %}

    INSTRUCTION variables.print -> STR
    %{
    	SVM_String s = ::svm_value_print(svm,_value);
    	return NEW_VALUE(string,s);
    %}

    INSTRUCTION variables.reset
    %{
    	::svm_variable_delete(svm,_value);
    	SVM_String s = ::svm_value_print(svm,_value);
    %}

Puis regénérez l'extension et recompilez la. Lancez maintenant l'application :

    ./variables.svm
    print: 17
    get: 17
    ### Simple Virtual Machine 1234 : PROCESS variables | 2023-01-01 00:00:00 GMT ########################################################################
    Kernel interrupted: @(main, line 7) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Core dump:
    Kernel:
    State: I, transmit_interruption, interrupted @(main, line 7) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Processor:
     State:
       Next instruction: <main:1/7> (Current one: <main:1/6>)
       Current memory  : &0*0
       Allocated memory: &0*2
       Aliases         : i s
       Flags           :
       Cascaded flags  :
       Local interruption handlers:
       Cascaded local interruption handlers:
     Saved states:
     Global interruption handlers:

    Code at <main:1/6>:
         :memory INT/i , STR/s      # <0> @(main, line 1)
         :variables.set 17  # <1> @(main, line 2)
         :variables.print -> &s     # <2> @(main, line 3)
         :com.message "print: " @&s # <3> @(main, line 4)
         :variables.get -> &i       # <4> @(main, line 5)
         :com.message "get: " @&i   # <5> @(main, line 6)
    ====== HERE ======
         :variables.reset   # <6> @(main, line 7)
    ==================
         :variables.print -> &s     # <7> @(main, line 8)
         :com.message "print: " @&s # <8> @(main, line 9)
     Labels:
     Symbols:

    Memory:
      &0: INT 17
      &1: STR "17"
     Aliases:
      i -> &0*1
      s -> &1*1
     Free space:

    ### Simple Virtual Machine 1234 : SYSTEM | 2023-01-01 00:00:00 GMT ###################################################################################
    Process variables interrupted: @(main, line 7) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

Cette fois, l'erreur apparaît bien directement dans l'instruction reset, après l'appel à la fonction `svm_variable_delete`. La variable a bien été supprimée, et la valeur correspondante également si la machine virtuelle ne la référence plus par ailleurs.

- Les variables Simple Virtual Machine ont deux portées possibles : locale (au bloc de code où elle est définie) ou globale (à l'extension entière).
- Les variables Simple Virtual Machine ont une portée locale par défaut.
- Les fonctions de l'interface programmatique `svm_variable_scope_set_global` et `svm_variable_scope_set_local` permettent de changer la portée d'une variable Simple Virtual Machine.
- La fonction de l'interface programmatique `svm_variable_delete` permet de révoquer immédiatement une variable Simple Virtual Machine.

# Variables SVM partagées

## Transformation en variable partagée

Les fonctions qui changent la portée d'une variable Simple Virtual Machine ne fonctionnent bien que lorsqu'une seule entité gère la portée d'une variable. En effet, les appels à ces fonctions sont idempotents par défaut : cela signifie qu'un seul appel à la fonction `svm_variable_scope_set_local` changera la portée d'une variable même si plusieurs appels à `svm_variable_scope_set_global` ont été faits sur cette variable.

Modifiez le code de l'extension pour obtenir :

    PLUGIN variables

    code:
    %{
    	SVM_Value _value = nullptr;
    %}

    DEFINE

    INSTRUCTION variables.set VALUE
    %{
    	_value = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    	return _value;
    %}

    INSTRUCTION variables.print -> STR
    %{
    	SVM_String s = ::svm_value_print(svm,_value);
    	return NEW_VALUE(string,s);
    %}

    INSTRUCTION variables.global
    %{
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.local
    %{
    	::svm_variable_scope_set_local(svm,_value);
    %}

Regénérez et recompilez l'extension, puis modifiez à nouveau le fichier variables.svm pour obtenir :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginvariables/libsvmvariables.so"
    PROCESS "variables"
    	CODE "main" INLINE
    		:memory INT/i, STR/s
    		:variables.set 17
    		:variables.print -> &s
    		:com.message "print 1: " @&s
    		:variables.global
    		:variables.print -> &s
    		:com.message "print 2: " @&s
    		:variables.global
    		:variables.print -> &s
    		:com.message "print 3: " @&s
    		:variables.local
    		:variables.print -> &s
    		:com.message "print 4: " @&s
    	END
    END

Enfin, lancez l'application et observez à quel moment l'interruption est levée :

    ./variables.svm
    print 1: 17
    print 2: 17
    print 3: 17
    ### Simple Virtual Machine 12345 : PROCESS variables | 2023-01-01 00:00:00 GMT #######################################################################
    Kernel interrupted: @(main, line 12) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Core dump:
    Kernel:
    State: I, transmit_interruption, interrupted @(main, line 12) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Processor:
     State:
       Next instruction: <main:1/12> (Current one: <main:1/11>)
       Current memory  : &0*0
       Allocated memory: &0*2
       Aliases         : i s
       Flags           :
       Cascaded flags  :
       Local interruption handlers:
       Cascaded local interruption handlers:
     Saved states:
     Global interruption handlers:

    Code at <main:1/11>:
         :memory INT/i , STR/s      # <0> @(main, line 1)
         :variables.set 17  # <1> @(main, line 2)
         :variables.print -> &s     # <2> @(main, line 3)
         :com.message "print 1: " @&s       # <3> @(main, line 4)
         :variables.global  # <4> @(main, line 5)
         :variables.print -> &s     # <5> @(main, line 6)
         :com.message "print 2: " @&s       # <6> @(main, line 7)
         :variables.global  # <7> @(main, line 8)
         :variables.print -> &s     # <8> @(main, line 9)
         :com.message "print 3: " @&s       # <9> @(main, line 10)
         :variables.local   # <10> @(main, line 11)
    ====== HERE ======
         :variables.print -> &s     # <11> @(main, line 12)
    ==================
         :com.message "print 4: " @&s       # <12> @(main, line 13)
     Labels:
     Symbols:

    Memory:
      &0: INT
      &1: STR "17"
     Aliases:
      i -> &0*1
      s -> &1*1
     Free space:

    ### Simple Virtual Machine 12345 : SYSTEM | 2023-01-01 00:00:00 GMT ##################################################################################
    Process variables interrupted: @(main, line 12) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

Un seul appel à la fonction `svm_variable_scope_set_local` a suffit à rendre la variable locale, alors que plusieurs appels à la fonction `svm_variable_scope_set_global` ont été réalisés.

Cela peut poser problème lorsque plusieurs entités veulent contrôler de manière indépendante la portée d'une variable : la variable doit être marquée comme partagée. Dans ce cas, il faudra un nombre équivalent d'appels à la fonction `svm_variable_scope_set_local` au nombre d'appels à la fonction `svm_variable_scope_set_global` pour que la variable redevienne locale.

Modifiez le code de l'extension :

    PLUGIN variables

    code:
    %{
    	SVM_Value _value = nullptr;
    %}

    DEFINE

    INSTRUCTION variables.set VALUE
    %{
    	_value = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_shared(svm,_value);
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    	return _value;
    %}

    INSTRUCTION variables.print -> STR
    %{
    	SVM_String s = ::svm_value_print(svm,_value);
    	return NEW_VALUE(string,s);
    %}

    INSTRUCTION variables.global
    %{
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.local
    %{
    	::svm_variable_scope_set_local(svm,_value);
    %}

Regénérez et recompilez l'extension, puis modifiez à nouveau le fichier variables.svm pour obtenir :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginvariables/libsvmvariables.so"
    PROCESS "variables"
    	CODE "main" INLINE
    		:memory INT/i, STR/s
    		:variables.set 17
    		:variables.print -> &s
    		:com.message "print 1: " @&s
    		:variables.global
    		:variables.print -> &s
    		:com.message "print 2: " @&s
    		:variables.global
    		:variables.print -> &s
    		:com.message "print 3: " @&s
    		:variables.local
    		:variables.print -> &s
    		:com.message "print 4: " @&s
    		:variables.local
    		:variables.print -> &s
    		:com.message "print 5: " @&s
    		:variables.local
    		:variables.print -> &s
    		:com.message "print 6: " @&s
    		:variables.local
    		:variables.print -> &s
    		:com.message "print 7: " @&s
    	END
    END

Et relancez l'application :

    ./variables.svm
    print 1: 17
    print 2: 17
    print 3: 17
    print 4: 17
    print 5: 17
    ### Simple Virtual Machine 12345 : PROCESS variables | 2023-01-01 00:00:00 GMT #######################################################################
    Kernel interrupted: @(main, line 18) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Core dump:
    Kernel:
    State: I, transmit_interruption, interrupted @(main, line 18) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

    Processor:
     State:
       Next instruction: <main:1/18> (Current one: <main:1/17>)
       Current memory  : &0*0
       Allocated memory: &0*2
       Aliases         : i s
       Flags           :
       Cascaded flags  :
       Local interruption handlers:
       Cascaded local interruption handlers:
     Saved states:
     Global interruption handlers:

    Code at <main:1/17>:
         :memory INT/i , STR/s      # <0> @(main, line 1)
         :variables.set 17  # <1> @(main, line 2)
         :variables.print -> &s     # <2> @(main, line 3)
         :com.message "print 1: " @&s       # <3> @(main, line 4)
         :variables.global  # <4> @(main, line 5)
         :variables.print -> &s     # <5> @(main, line 6)
         :com.message "print 2: " @&s       # <6> @(main, line 7)
         :variables.global  # <7> @(main, line 8)
         :variables.print -> &s     # <8> @(main, line 9)
         :com.message "print 3: " @&s       # <9> @(main, line 10)
         :variables.local   # <10> @(main, line 11)
         :variables.print -> &s     # <11> @(main, line 12)
         :com.message "print 4: " @&s       # <12> @(main, line 13)
         :variables.local   # <13> @(main, line 14)
         :variables.print -> &s     # <14> @(main, line 15)
         :com.message "print 5: " @&s       # <15> @(main, line 16)
         :variables.local   # <16> @(main, line 17)
    ====== HERE ======
         :variables.print -> &s     # <17> @(main, line 18)
    ==================
         :com.message "print 6: " @&s       # <18> @(main, line 19)
         :variables.local   # <19> @(main, line 20)
         :variables.print -> &s     # <20> @(main, line 21)
         :com.message "print 7: " @&s       # <21> @(main, line 22)
     Labels:
     Symbols:

    Memory:
      &0: INT
      &1: STR "17"
     Aliases:
      i -> &0*1
      s -> &1*1
     Free space:

    ### Simple Virtual Machine 12345 : SYSTEM | 2023-01-01 00:00:00 GMT ##################################################################################
    Process variables interrupted: @(main, line 18) Interruption FAILURE not handled: Invalid call to API function svm_value_print: value is not a value.

Cette fois, trois appels à la fonction `svm_variable_scope_set_local` ont été nécessaires pour rendre à nouveau la variable locale.

## Test de portée

Parfois, certaines opérations (comme des libérations de ressources) ne doivent être entreprises que lorsqu'une variable redevient locale. Or, la fonction `svm_variable_scope_set_local` ne précise pas la portée de la variable.

Le test de portée peut être réalisé grâce à la fonction `svm_variable_scope_is_local`.

Modifiez le code de l'extension :

    PLUGIN variables

    code:
    %{
    	SVM_Value _value = nullptr;
    %}

    DEFINE

    INSTRUCTION variables.set VALUE
    %{
    	_value = ::svm_parameter_value_get(svm,argv[0]);
    	::svm_variable_scope_set_shared(svm,_value);
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.get -> VALUE
    %{
    	return _value;
    %}

    INSTRUCTION variables.print -> STR
    %{
    	SVM_String s = ::svm_value_print(svm,_value);
    	return NEW_VALUE(string,s);
    %}

    INSTRUCTION variables.global
    %{
    	::svm_variable_scope_set_global(svm,_value);
    %}

    INSTRUCTION variables.local -> BLN
    %{
    	::svm_variable_scope_set_local(svm,_value);
    	return NEW_VALUE(boolean,::svm_variable_scope_is_local(svm,_value));
    %}

Regénérez et recompilez l'extension une dernière fois, puis modifiez enfin le fichier variables.svm pour obtenir :

    #!/usr/bin/env svm
    LOG
    PLUGIN "svmcom.so"
    LOCAL PLUGIN "svmpluginvariables/libsvmvariables.so"
    PROCESS "variables"
    	CODE "main" INLINE
    		:memory INT/i, STR/s, BLN/b
    		:variables.set 17
    		:variables.print -> &s
    		:com.message "print 1: " @&s
    		:variables.global
    		:variables.print -> &s
    		:com.message "print 2: " @&s
    		:variables.global
    		:variables.print -> &s
    		:com.message "print 3: " @&s
    		:variables.local -> &b
    		:shutdown :when @&b TRUE
    		:variables.print -> &s
    		:com.message "print 4: " @&s
    		:variables.local -> &b
    		:shutdown :when @&b TRUE
    		:variables.print -> &s
    		:com.message "print 5: " @&s
    		:variables.local -> &b
    		:shutdown :when @&b TRUE
    		:variables.print -> &s
    		:com.message "print 6: " @&s
    		:variables.local -> &b
    		:shutdown :when @&b TRUE
    		:variables.print -> &s
    		:com.message "print 7: " @&s
    	END
    END

Lancez l'application :

    ./variables.svm
    print 1: 17
    print 2: 17
    print 3: 17
    print 4: 17
    print 5: 17

L'application arrête le processeur lorsque la variable redevient locale, évitant l'interruption.

- Une variable Simple Virtual Machine partagée est une variable dont la portée globale ou locale est gérée indépendamment par plusieurs entités.
- Une variable est partagée lorsque la fonction `svm_variable_scope_set_shared` est appellée sur la variable après sa création.
- Pour devenir locale, une variable partagée doit être rendue locale autant de fois qu'elle a été rendue globale. Pour les variables non partagées, un seul appel aux fonctions `svm_variable_scope_set_local` et `svm_variable_scope_set_global` modifient la portée de la variable.
- Pour détecter quand une variable partagée redevient locale, la fonction `svm_variable_scope_is_local` peut être invoquée sur la variable.
- Une variable partagée peut être révoquée immédiatement en utilisant la fonction `svm_variable_delete`.

# Conclusion

Vous venez de voir les variables Simple Virtual Machine permettant d'accéder aux valeurs de la machine virtuelle depuis le code des extensions.

L'interêt principal des variables Simple Virtual Machine est la suppression automatique des valeurs une fois qu'elles ne sont plus nécessaires, que ce soit en C ou en C++.

Cependant, ce confort d'utilisation vient avec une complexité qui se manifeste lorsqu'une variable a une portée qui dépasse le bloc de code où elle est définie.

La maîtrise de la portée des variables peut sembler être un peu tel un casse-tête au départ, mais la plupart des utilisations de variables globales suivent des schémas simples qui seront présentés dans des didacticiels ultérieurs.