Im vorherigen Artikel Ausführen von JavaVM mit WebAssembly habe ich versucht, einen Java-Interpreter (JavaVM) mithilfe von WebAssembly zu erstellen, aber für später ein wenig Einfallsreichtum Oder bewahren Sie beim Erweitern ein Memorandum als Referenz auf. Der WebAssembly-Compiler geht übrigens von "Emscripten" aus.
Eine Reihe von Quelldateien in C-Sprache von Java Interpreter (Java VM) usw. wurde auf den folgenden GitHub hochgeladen. https://github.com/poruruba/javaemu
Wir verwenden die folgenden Verknüpfungen.
Aufrufen von C-Sprachfunktionen aus Javascript
Speicherfreigabe zwischen Javascript und C-Sprache
Es scheint, dass Sie Javascript-Funktionen auch aus der C-Sprache aufrufen können, aber ich habe es diesmal nicht verwendet.
Zunächst wird die Funktionsdefinition auf der C-Sprachseite aufgerufen. Fügen Sie der Funktionsdefinition EMSCRIPTEN_KEEPALIVE hinzu. Nur das.
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
(Da jedoch die Ausdruckskraft des Rückgabewerts von der C-Sprache zu Javascript schlecht ist, werden Parameter unter Verwendung des später beschriebenen gemeinsamen Speichers ausgetauscht.)
Der Aufruf von der Javascript-Seite sieht wie folgt aus. Das in javaemu.js definierte Dienstprogramm "Module", das zur Kompilierungszeit von emcc generiert wird, ist nützlich.
main.js
var ret = Module.ccall('setRomImage', "number", ["number", "number"], [g_pointer, bin.length]);
Das zweite Argument gibt den Rückgabetyp der C-Sprachfunktion an. Das dritte Argument zeigt den Typ der Liste der Argumente für die C-Sprachfunktion. Das vierte Argument ist der Wert, den Sie tatsächlich an das Argument der C-Sprachfunktion übergeben.
Die Funktion ccall ist eine der zur Kompilierungszeit angegebenen Funktionen.
s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'UTF8ArrayToString', 'stringToUTF8Array']"
Ich benutze es wie folgt.
(Referenzinformationen) https://emscripten.org/docs/porting/connecting_cpp_and_javascript/Interacting-with-code.html#access-memory-from-javascript
//Zum Beispiel 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]);
Rufen Sie Module._malloc auf, um den Speicher zuzuweisen, den Sie freigeben möchten. Dies ist in Ordnung für vorzeichenlose Zeichen-Arrays, aber für Shorts und Longs müssen Sie die Bereichsgröße entsprechend angeben. Durch Aufrufen von Module.HEAPU8.set legt die Javascript-Seite den Wert des Arrays fest, das Sie im Speicher festlegen möchten. Da es sich um ein vorzeichenloses Zeichenarray handelt, wird beim Zugriff auf den Speicher ein Makro namens HEAPU8 verwendet.
Sie können es auch wie folgt einstellen.
Module.HEAPU8[g_pointer + ptr] = 123;
Speichern Sie auf der Seite der C-Sprache in der Funktion von setRomImage die Adresse des gemeinsam genutzten Speichers und verweisen Sie dann auf diese oder legen Sie sie fest.
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;
}
Die Funktion setRomImage wird zum Freigeben der Klassendatei in der Jar-Datei verwendet, und die Funktion setInoutBuffer wird zum Austauschen des Arguments beim Aufrufen der Funktion callStaticMain und des Rückgabewerts von der C-Sprachseite verwendet.
C-Sprache fopen ist ebenfalls verfügbar, der Browser arbeitet jedoch in Sandbox, sodass auf den PC nicht auf lokale Dateien zugegriffen werden kann. Stattdessen können Sie zur Kompilierungszeit eine Bilddatei (javaemu.data) der Ordnerstruktur und der Dateien als Snapshot erstellen und zur Laufzeit mit fopen lesen.
(Referenzinformationen) https://emscripten.org/docs/porting/files/index.html
Das Folgende ist ein Auszug aus diesem Teil.
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 ist das, was zur Kompilierungszeit in emcc angegeben wurde.
emcc *.c -s WASM=1 -o javaemu.js -s "EXTRA_EXPORTED_RUNTIME_METHODS=['ccall', 'UTF8ArrayToString', 'stringToUTF8Array']" --preload-file pre_classes
Es ist keine WebAssembly, sondern eine Implementierungsspezifikation des implementierten Java-Interpreters.
Verwenden Sie die Funktion callStaticMain.
main.c
int EMSCRIPTEN_KEEPALIVE callStaticMain(char *className, char *param )
In diesem Fall wird JavaVM, ein Java-Interpreter, initialisiert, und die Funktionen von Java-Klassen werden in den folgenden Teilen aufgerufen.
main.c
ret = startStaticMain(className, param, &retType, &retVar);
Legen Sie im Argument den aufzurufenden Klassennamen und das Argument fest, das Sie übergeben möchten. Der Funktionsname und -typ auf der Java-Seite, die von dieser aufgerufen werden, sind festgelegt.
public static void main( String[] args ){
Daher ist es erforderlich, den obigen Funktionsnamen in der Java-Quelle der Zielklasse zu implementieren. Implementieren Sie die Logik, die Sie in dieser Java-Funktion verarbeiten möchten. Argumentaustausch
String[] input = System.getInput(-1);
Dann holen Sie sich das Argument von der Javascript-Seite und
System.setOutput(new String[]{ "World", "Guten Abend" });
Gibt den Rückgabewert an die Javascript-Seite zurück.
Sie können einige Dinge mit der Java-Implementierung tun, aber ich möchte trotzdem die native Verarbeitung in der C-Sprache implementieren. Definieren Sie dazu die folgende Funktion in der Java-Quelle. Der Funktionsname kann beliebig sein. Kurz gesagt, fügen Sie den Modifikator native static hinzu.
public native static final String[] getInput(int max);
Wenn Sie es noch nicht nativ implementieren, zeigt die Entwicklerkonsole Ihres Browsers einen Fehler ähnlich dem folgenden an und der Interpreter wird beendet.
** Native Method Missing:
javaemu.js:2178 // base/framework/System_getInput_(I)[Ljava/lang/String;
javaemu.js:2178 { 320190814UL, func },
Dies bedeutet, dass es keine Definition der nativen Funktion gibt. Implementieren Sie die native Funktion in der C-Sprache und setzen Sie den Zeiger der implementierten C-Sprachfunktion im folgenden Teil von waba_native.c.
waba_native.c
NativeMethod nativeMethods[] = {
//・ ・ ・ ・ ・ ・ ・
// base/framework/System_printStackTrace_()V
{ 320187986UL, FCSystem_printStackTrace },
// base/framework/System_getInput_(I)[Ljava/lang/String; //★ Ergänzung
{ 320190814UL, FCSystem_getInput }, //★ Ergänzung
// base/framework/System_sleep_(I)I
{ 320192265UL, FCSystem_sleep },
//・ ・ ・ ・ ・ ・ ・
};
NativeMethod nativeMethods [] verfügt über native Funktionsimplementierungen in C-Sprache. Sie sind in aufsteigender Reihenfolge der Hash-Werte angeordnet. Fügen Sie sie daher an den entsprechenden Stellen ein. (Sonst findet der Dolmetscher es nicht)
Die Implementierung nativer Funktionen in der Sprache C sieht beispielsweise wie folgt aus.
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;
}
Bitte beachten Sie andere native Funktionen. Die Eingabeargumente befinden sich im Var-Stapel []. Stellen Sie die Antwort auf Stapel [0]. (Da es ← ist, ist es nicht möglich, eine native Funktion ohne Argumente zu erstellen.) Wenn es kein Problem gibt, wird 0 zurückgegeben.
JavaVM verfügt über eingeschränkte Funktionen. Beispielsweise wird Bytecode mit float, double und long nicht unterstützt. Wie Sie anhand der folgenden Funktion sehen können, sind alle oben genannten Bytecodes auskommentiert.
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:
//・ ・ ・
das ist alles
Recommended Posts