Ausführen von JavaVM mit WebAssembly: Ergänzung

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

Zusammenarbeit zwischen Javascript und C-Sprache

Wir verwenden die folgenden Verknüpfungen.

Aufrufen von C-Sprachfunktionen aus Javascript

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']"

Speicherfreigabe zwischen Javascript und C-Sprache

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.

Offenes Pseudoverhalten

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

Java Interpreter Ergänzung

Es ist keine WebAssembly, sondern eine Implementierungsspezifikation des implementierten Java-Interpreters.

Java-Funktionsaufruf

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.

Native Erweiterung des Java-Interpreters

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.

Einschränkungen für Java-Interpreter (Java VM)

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

Ausführen von JavaVM mit WebAssembly: Ergänzung
Führen Sie Java VM mit Web Assembly aus
Profilerstellung mit Java Visual VM ~ Grundlegende Verwendung ~
Installieren Sie Java mit Homebrew
Wechseln Sie die Plätze mit Java
Installieren Sie Java mit Ansible
Führen Sie Maven unter Java 8 aus, während Sie unter Java 6 kompilieren und unter Java 11 testen
Bequemer Download mit JAVA
Schalten Sie Java mit direnv
Lass uns mit Java kratzen! !!
Erstellen Sie Java mit Wercker
Endian-Konvertierung mit JAVA
Erstellen Sie mit Gradle ein Java-Multiprojekt
Erste Schritte mit Java Collection
Grundlegende Authentifizierung mit Java 11 HttpClient
Experimentieren wir mit der Java-Inline-Erweiterung
Führen Sie Batch mit Docker-Compose mit Java-Batch aus
[Vorlage] MySQL-Verbindung mit Java
Schreiben Sie Java Try-Catch mit Optional neu
Installieren Sie Java 7 mit Homebrew (Fass)
[Java] JSON-Kommunikation mit Jackson
Java zum Spielen mit Function
Versuchen Sie eine DB-Verbindung mit Java
Aktivieren Sie Java EE mit NetBeans 9
[Java] JavaConfig mit statischer innerer Klasse
Versuchen Sie gRPC mit Java, Maven
Lassen Sie uns Excel mit Java betreiben! !!
Java-Versionsverwaltung mit SDKMAN
RSA-Verschlüsselung / Entschlüsselung mit Java 8
Paging PDF mit Java + PDFBox.jar
Sortieren Sie Zeichenfolgen als charakteristische Funktion mit Java
Objektorientiert mit Strike Gundam (Java)
[Java] Inhaltserfassung mit HttpCliient
Fehlerbehebung mit Java Flight Recorder
Optimieren Sie Java-Tests mit Spock
Stellen Sie mit Java eine Verbindung zur Datenbank her
Fehler beim Spielen mit Java
Verwenden von Mapper mit Java (Spring)
Java Study Memo 2 mit Progate
Erste Schritte mit Java Basics
Saisonale Anzeige mit Java-Schalter
Verwenden Sie SpatiaLite mit Java / JDBC
Lernen von Java mit Progate Note 1
Vergleichen Sie Java 8 Optional mit Swift
HTML-Analyse (Scraping) mit JAVA
Bildschirmübergang mit Swing, Java
Java Unit Test mit Mockito