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
Nous utilisons les liens suivants.
Appel des fonctions du langage C à partir de Javascript
Partage de mémoire entre Javascript et langage C
Il semble que vous puissiez également appeler des fonctions Javascript à partir du langage C, mais cette fois je ne l'ai pas utilisé.
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']"
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.
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
Ce n'est pas un WebAssembly, mais une spécification d'implémentation de l'interpréteur Java implémenté.
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.
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é.
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