Exécution de JavaVM avec WebAssembly: supplément

Dans l'article précédent Exécution de JavaVM avec WebAssembly, j'ai essayé de créer un interpréteur Java (JavaVM) en utilisant WebAssembly, mais pour plus tard, un peu d'ingéniosité Ou gardez un mémorandum pour référence lors de l'expansion. Au fait, le compilateur WebAssembly suppose "Emscripten".

Un ensemble de fichiers source en langage C de l'interpréteur Java (Java VM), etc. a été téléchargé sur le GitHub suivant.  https://github.com/poruruba/javaemu

Coopération entre Javascript et langage C

Nous utilisons les liens suivants.

Appel des fonctions du langage C à partir de Javascript

La première est la définition de fonction du côté du langage C qui est appelée. Ajoutez EMSCRIPTEN_KEEPALIVE à la définition de fonction. Seulement ça.

main.c


#include <emscripten/emscripten.h>

#ifdef __cplusplus
extern "C" {
#endif

int EMSCRIPTEN_KEEPALIVE setInoutBuffer(const unsigned char *buffer, long bufferSize );
int EMSCRIPTEN_KEEPALIVE setRomImage(const unsigned char *romImage, long romSize );
int EMSCRIPTEN_KEEPALIVE callStaticMain(char *className, char *param );

#ifdef __cplusplus
}
#endif

(Cependant, comme l'expressivité de la valeur de retour du langage C vers Javascript est médiocre, les paramètres sont échangés à l'aide de la mémoire partagée décrite plus loin.)

L'appel du côté Javascript ressemble à ce qui suit. L'utilitaire "Module" défini dans javaemu.js généré lors de la compilation par emcc est utile.

main.js


var ret = Module.ccall('setRomImage', "number", ["number", "number"], [g_pointer, bin.length]);

Le deuxième argument indique le type de retour de la fonction de langage C. Le troisième argument montre le type de la liste d'arguments pour la fonction de langage C. Le quatrième argument est la valeur que vous passez réellement à l'argument de la fonction de langage C.

La fonction appelée ccall est l'une des fonctions spécifiées au moment de la compilation.

s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'UTF8ArrayToString', 'stringToUTF8Array']"

Partage de mémoire entre Javascript et langage C

Je l'utilise comme suit.

(Informations de référence)  https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#access-memory-from-javascript

//Par exemple, var bin= { array: [1, 2, 3, 4] };
g_pointer = Module._malloc(bin.array.length);
Module.HEAPU8.set(bin.array, g_pointer);
var ret = Module.ccall('setRomImage', "number", ["number", "number"], [g_pointer, bin.array.length]);

Appelez Module._malloc pour allouer la mémoire que vous souhaitez partager. Cela convient aux tableaux de caractères non signés, mais pour les courts et les longs, vous devez spécifier la taille de la zone en conséquence. En appelant Module.HEAPU8.set, le côté Javascript définit la valeur du tableau que vous souhaitez définir dans la mémoire. Puisqu'il s'agit d'un tableau de caractères non signé, une macro appelée HEAPU8 est utilisée lors de l'accès à la mémoire.

Vous pouvez également le définir comme suit.

Module.HEAPU8[g_pointer + ptr] = 123;

Côté langage C, dans la fonction de setRomImage, l'adresse de la mémoire partagée est mémorisée, puis référencée et définie par la suite.

main.c


int EMSCRIPTEN_KEEPALIVE setRomImage(const unsigned char *romImage, long romSize )
{
	FUNC_CALL();
	
	classRom_ext = (unsigned char*)romImage;
	classRomSize_ext = romSize;

	FUNC_RETURN();
	
	return FT_ERR_OK;
}

La fonction setRomImage est utilisée pour partager le fichier de classe dans le fichier Jar, et la fonction setInoutBuffer est utilisée pour échanger l'argument lors de l'appel de la fonction callStaticMain et la valeur de retour du côté du langage C.

Comportement pseudo de Fopen

Le langage C fopen est également disponible, mais le navigateur fonctionne dans Sandbox, il n'est donc pas possible d'accéder aux fichiers locaux sur le PC. Au lieu de cela, vous pouvez créer un fichier image (javaemu.data) de la structure du dossier et des fichiers au moment de la compilation en tant qu'instantané et le lire avec fopen au moment de l'exécution.

(Informations de référence)  https://emscripten.org/docs/porting/files/index.html

Ce qui suit est un extrait de cette partie.

alloc_class.c


#define PRECLASS_DIR	"pre_classes"

char *baseClassDir = PRECLASS_DIR;

static long file_read(const char *p_fname, unsigned char **pp_bin, long *p_size)
{
	FILE *fp;
	long fsize;
	char path[255];

	strcpy(path, baseClassDir);
	strcat(path, "/");
	strcat(path, p_fname);
	strcat(path, ".class");

	fp = fopen(path, "rb");
	if (fp == NULL)
		return FT_ERR_NOTFOUND;
//・ ・ ・

pre_classes est celle qui a été spécifiée lors de la compilation dans emcc.

emcc *.c -s WASM=1 -o javaemu.js -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'UTF8ArrayToString', 'stringToUTF8Array']" --preload-file pre_classes

Supplément d'interprétation Java

Ce n'est pas un WebAssembly, mais une spécification d'implémentation de l'interpréteur Java implémenté.

Appel de fonction Java

Utilisez la fonction callStaticMain.

main.c


int EMSCRIPTEN_KEEPALIVE callStaticMain(char *className, char *param )

En cela, JavaVM, qui est un interpréteur Java, est initialisé et les fonctions des classes Java sont appelées dans les parties suivantes.

main.c


	ret = startStaticMain(className, param, &retType, &retVar);

Dans l'argument, définissez le nom de la classe à appeler et l'argument à transmettre. Le nom et le type de la fonction du côté Java qui sont appelés par cela sont fixes.

  public static void main( String[] args ){

Par conséquent, il est nécessaire d'implémenter le nom de fonction ci-dessus dans la source Java de la classe cible. Implémentez la logique que vous souhaitez traiter dans cette fonction Java. Échange d'arguments

String[] input = System.getInput(-1);

Ensuite, récupérez l'argument du côté Javascript et

System.setOutput(new String[]{ "World", "Bonsoir" });

Renvoie la valeur de retour du côté Javascript.

Extension native de l'interpréteur Java

Vous pouvez faire certaines choses avec l'implémentation Java, mais je souhaite toujours implémenter le traitement natif en utilisant le langage C. Pour ce faire, définissez la fonction suivante dans la source Java. Le nom de la fonction peut être n'importe quoi. En bref, ajoutez le modificateur native static.

public native static final String[] getInput(int max);

Si vous ne l'implémentez pas encore nativement, la console développeur de votre navigateur affichera une erreur similaire à la suivante et l'interpréteur se fermera.

** Native Method Missing:
javaemu.js:2178 // base/framework/System_getInput_(I)[Ljava/lang/String;
javaemu.js:2178 { 320190814UL, func },

Cela signifie qu'il n'y a pas de définition de la fonction native. Implémentez la fonction native en langage C et définissez le pointeur de la fonction de langage C implémentée dans la partie suivante de waba_native.c.

waba_native.c


NativeMethod nativeMethods[] = {

//・ ・ ・ ・ ・ ・ ・
	// base/framework/System_printStackTrace_()V
	{ 320187986UL, FCSystem_printStackTrace },
	// base/framework/System_getInput_(I)[Ljava/lang/String; //★ Ajout
	{ 320190814UL, FCSystem_getInput },  //★ Ajout
	// base/framework/System_sleep_(I)I
	{ 320192265UL, FCSystem_sleep },

//・ ・ ・ ・ ・ ・ ・

};

NativeMethod nativeMethods [] a des implémentations de fonctions natives en langage C. Ils sont classés par ordre croissant de valeurs de hachage, insérez-les donc aux endroits appropriés. (Sinon l'interprète ne le trouvera pas)

L'implémentation des fonctions natives en langage C ressemble par exemple à ce qui suit.

waba_native.c


// base/framework/System_getInput_(I)[Ljava/lang/String;
long FCSystem_getInput(Var stack[]) {
	Var v;
	unsigned char num;
	unsigned char i;
	WObject strArray;
	WObject *obj;
	unsigned long ptr;
	long max;

	if (inoutBuff_ext == NULL)
		return ERR_CondNotSatisfied;

	max = stack[0].intValue;
	num = inoutBuff_ext[0];
	if (max >= 0 && num > max)
		num = (unsigned char)max;
	strArray = createArrayObject(TYPE_OBJECT, num);
	obj = (WObject *)WOBJ_arrayStart(strArray);

	if (pushObject(strArray) != FT_ERR_OK)
		return ERR_OutOfObjectMem;

	ptr = 1;
	for (i = 0; i < num; i++) {
		obj[i] = createString((const char*)&inoutBuff_ext[ptr]);
		if (obj[i] == WOBJECT_NULL) {
			popObject();
			return ERR_OutOfObjectMem;
		}
		ptr += strlen((const char*)&inoutBuff_ext[ptr]) + 1;
	}

	popObject();

	v.obj = strArray;
	stack[0] = v;

	return 0;
}

Veuillez vous référer à d'autres fonctions natives. Les arguments d'entrée sont dans la pile Var []. Définissez la réponse sur la pile [0]. (Parce que c'est ←, il n'est pas possible de créer une fonction native sans arguments) S'il n'y a pas de problème, renvoie 0; est renvoyé.

Restrictions de l'interpréteur Java (Java VM)

JavaVM a des fonctionnalités limitées. Par exemple, le bytecode utilisant float, double et long n'est pas pris en charge. Comme vous pouvez le voir en regardant la fonction ci-dessous, tous les codes d'octets ci-dessus sont commentés.

waba.c


long executeMethod(WClass *wclass, WClassMethod *method, Var params[], unsigned short numParams, unsigned char *retType, Var* retValue) {
//・ ・ ・
		// NOTE: this is the full list of unsupported opcodes. Adding all
		// these cases here does not cause the VM executable code to be any
		// larger, it just makes sure that the compiler uses a jump table
		// with no spaces in it to make sure performance is as good as we
		// can get (tested under Codewarrior for PalmOS).
/*
		case OP_lconst_0:
		case OP_lconst_1:
		case OP_dconst_0:
//・ ・ ・

c'est tout

Recommended Posts

Exécution de JavaVM avec WebAssembly: supplément
Exécuter Java VM avec Web Assembly
Profilage avec Java Visual VM ~ Utilisation de base ~
Installez java avec Homebrew
Changer de siège avec Java
Installez Java avec Ansible
Exécutez Maven sur Java 8 lors de la compilation sur Java 6 et des tests sur Java 11
Téléchargement confortable avec JAVA
Changer java avec direnv
Raclons avec Java! !!
Construire Java avec Wercker
Conversion Endian avec JAVA
Créer un multi-projet Java avec Gradle
Premiers pas avec Java Collection
Authentification de base avec Java 11 HttpClient
Expérimentons l'expansion en ligne Java
Exécuter un lot avec docker-compose avec Java batch
[Template] Connexion MySQL avec Java
Réécrire Java try-catch avec facultatif
Installez Java 7 avec Homebrew (cask)
[Java] Communication JSON avec jackson
Java pour jouer avec Function
Essayez la connexion DB avec Java
Activer Java EE avec NetBeans 9
[Java] JavaConfig avec classe interne statique
Essayez gRPC avec Java, Maven
Exploitons Excel avec Java! !!
Gestion des versions Java avec SDKMAN
Cryptage / décryptage RSA avec Java 8
Pagination de PDF avec Java + PDFBox.jar
Trier les chaînes comme une fonction caractéristique avec Java
Orienté objet avec Strike Gundam (java)
[Java] Acquisition de contenu avec HttpCliient
Dépannage avec Java Flight Recorder
Rationalisez les tests Java avec Spock
Connectez-vous à DB avec Java
Erreur lors de la lecture avec java
Utilisation de Mapper avec Java (Spring)
Mémo d'étude Java 2 avec Progate
Premiers pas avec les bases de Java
Affichage saisonnier avec commutateur Java
Utiliser SpatiaLite avec Java / JDBC
Étudier Java avec Progate Note 1
Comparez Java 8 en option avec Swift
Analyse HTML (scraping) avec JAVA
Transition d'écran avec swing, java
Test unitaire Java avec Mockito