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 manipuler le code Simple Virtual Machine depuis une extension.

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 code.svm_plugin en utilisant ce code :

PLUGIN code

DEFINE

Obtenir un code

Depuis une valeur

Modifiez le code de l'extension :

PLUGIN code

DEFINE

INSTRUCTION code.library LIB
%{
	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,l);
%}

INSTRUCTION code.symbol SYM
%{
	SVM_Value_Symbol s = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_symbol_get_code(svm,s);
%}

Pour le moment, vu que les instructions se contentent de récupérer le code sans agir dessus, il n'est pas nécessaire de générer l'extension et de la compiler.

Notez également, dans l'interface programmatique, la présence des fonctions svm_value_symbol_get_address qui permet de récupérer l'adresse de l'instruction dans le code et svm_value_symbol_get_location qui permet de récupérer sous forme de chaine la localisation du symbole dans le code.

Depuis le processeur

Modifiez le code de l'extension :

PLUGIN code

DEFINE

INSTRUCTION code.current -> LIB
%{
	SVM_Code c = ::svm_processor_get_currentcode(svm,CURRENT(kernel));
	return NEW_VALUE(library,c);
%}

Générez le code de l'extension et compilez la. Voici une petite application permettant de tester cette instruction :

#!/usr/bin/env svm
LOG
LOCAL PLUGIN "svmplugincode/libsvmcode.so"
PLUGIN "svmcom.so"
PROCESS "code"
	CODE "main" INLINE
		:debug BREAK
		:memory LIB/l
		:code.current -> &l
		:goto $(@&l/"s")
		:com.message "avoided"
	:symbol s
		:com.message "reached"
	END
END

Lancez l'application. Puis, modifiez le code de l'application pour remplacer l'instruction de saut par :goto $"s". Le comportement de l'application est le même.

Par compilation

Modifiez le code de l'extension :

PLUGIN code

DEFINE

INSTRUCTION code.new STR:name STR:source -> LIB
%{
	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Value_String s = ::svm_parameter_value_get(svm,argv[1]);
	SVM_Code c = ::svm_code_new(svm,n,s);
	return NEW_VALUE(library,c);
%}

INSTRUCTION code.compile STR:name STR:source -> [ LIB STR ]
%{
	SVM_Value_String n = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Value_String s = ::svm_parameter_value_get(svm,argv[1]);
	SVM_Variable c = ::svm_code_compile(svm,n,s);
	if(::svm_variable_type_is_code(svm,c))
	{
		return NEW_VALUE(library,c);
	}
	return c;
%}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile un bout de code avec les deux instructions, puis qui réalise un appel de fonction à l'intérieur de ces code compilés.

Vous pouvez noter que la différence entre les deux fonctions svm_code_new et svm_code_compile réside dans leur gestion des erreurs de compilation. La première lève une interruption, alors que la seconde rapporte l'erreur sous forme d'une chaine, permettant à l'instruction de continuer son exécution malgré un code erroné.

Nom et code

Modifiez le code de l'extension :

PLUGIN code

DEFINE

INSTRUCTION code.name LIB -> STR
%{
	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,l);
	SVM_String n = ::svm_code_get_name(svm,c);
	return NEW_VALUE(string,n);
%}

INSTRUCTION code.source LIB -> STR
%{
	SVM_Value_Library l = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,l);
	SVM_String s = ::svm_code_get_source(svm,c);
	return NEW_VALUE(string,s);
%}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile un bout de code avec l'instruction :library et affiche le résultat des deux instructions.

Pour retrouver le code avec toutes les annotations, il faut plutôt utiliser la fonction svm_code_print.

Etiquettes et symboles

Modifiez le code de l'extension :

PLUGIN code

DEFINE

INSTRUCTION code.label LIB STR -> INT ?
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Value_String l = ::svm_parameter_value_get(svm,argv[1]);
	SVM_Boolean b = ::svm_code_label_has_address(svm,c,l);
	if(not b)
	{
		return NEW_NULL_VALUE(integer);
	}
	SVM_Address a = ::svm_code_label_get_address(svm,c,l);
	return NEW_VALUE(integer,a);
%}

INSTRUCTION code.labels LIB -> PTR
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Value_String *l = ::svm_code_label_list(svm,c);
	SVM_Size s = 0;
	for(SVM_Value_String *it=l ; *it ; ++it)
	{
		::svm_value_state_set_movable(svm,*it);
		++s;
	}
	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
	::svm_memory_zone_append_internal__raw(svm,z,STRING,s);
	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
	::svm_memory_write_pointer(svm,CURRENT(kernel),p,l);
	return p;
%}

INSTRUCTION code.symbol LIB STR -> SYM ?
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Value_String l = ::svm_parameter_value_get(svm,argv[1]);
	SVM_Boolean b = ::svm_code_symbol_has_address(svm,c,l);
	if(not b)
	{
		return NEW_NULL_VALUE(symbol);
	}
	SVM_Value_Symbol s = ::svm_code_symbol_get_address(svm,c,l);
	return s;
%}

INSTRUCTION code.symbols LIB -> PTR
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Value_String *l = ::svm_code_symbol_list(svm,c);
	SVM_Size s = 0;
	for(SVM_Value_String *it=l ; *it ; ++it)
	{
		::svm_value_state_set_movable(svm,*it);
		++s;
	}
	SVM_Memory_Zone z = ::svm_memory_zone_new(svm);
	::svm_memory_zone_append_internal__raw(svm,z,STRING,s);
	SVM_Value_Pointer p = ::svm_memory_allocate(svm,CURRENT(kernel),z);
	::svm_memory_write_pointer(svm,CURRENT(kernel),p,l);
	return p;
%}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile un bout de code ayant plusieurs étiquettes et symboles avec l'instruction :library. Ensuite, essayez d'appeller ces instructions sur la bibliothèque.

Vous pouvez alors remarquer que :

Introspection

L'interface programmatique offre quelques fonctions permettant de décortiquer le contenu du code.

Modifiez le code de l'extension :

PLUGIN code

DEFINE

INSTRUCTION code.size LIB -> INT
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Size s = ::svm_code_get_size(svm,c);
	return NEW_VALUE(integer,s);
%}

INSTRUCTION code.text LIB INT -> STR
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	auto a = ARGV_VALUE(1,integer);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_String t = ::svm_code_instruction_get_text(svm,c,a);
	return NEW_VALUE(string,t);
%}

INSTRUCTION code.location LIB INT -> STR
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	auto a = ARGV_VALUE(1,integer);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_String l = ::svm_code_instruction_get_location(svm,c,a);
	return NEW_VALUE(string,l);
%}

INSTRUCTION code.system LIB INT -> BLN
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	auto a = ARGV_VALUE(1,integer);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Boolean s = ::svm_code_instruction_is_system(svm,c,a);
	return NEW_VALUE(boolean,s);
%}

INSTRUCTION code.waiting LIB INT -> BLN
%{
	SVM_Value_Library lib = ::svm_parameter_value_get(svm,argv[0]);
	auto a = ARGV_VALUE(1,integer);
	SVM_Code c = ::svm_value_library_get_code(svm,lib);
	SVM_Boolean w = ::svm_code_instruction_is_waiting(svm,c,a);
	return NEW_VALUE(boolean,w);
%}

Générez le code de l'extension et compilez la. Ecrivez une petite application qui compile du code dans une bibliothèque, puis qui récupère la taille du code. Pour chaque entier entre zéro et cette taille moins un, appellez les autres instructions pour afficher l'adresse, le texte de l'instruction, sa localisation dans le code, et ses drapeaux système et attente.

Vous remarquerez que les étiquettes et les symboles n'apparaissent pas dans la liste des instructions.

Conclusion

Vous venez de voir comment manipuler le code de la machine virtuelle depuis les extensions.

Savoir compiler du code au sein d'une instruction et le manipuler au besoin peut être très utile pour créer des portions modulaires d'une application.